Ignore c719731e
diff --git a/Android.mk b/Android.mk
index 28d2d8e..a6cfb4a 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,39 @@
 	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
+
+# Android's gl2.h provides the new glShaderSource signature
+LOCAL_CFLAGS += -DGR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE=1
+
+# 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 +104,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 +119,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 +157,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 +166,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 +203,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 +275,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 +316,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 +445,7 @@
 	src/opts/SkUtils_opts_none.cpp
 endif
 
+
 # these are for emoji support, needed by webkit
 LOCAL_SRC_FILES += \
 	emoji/EmojiFont.cpp
@@ -285,7 +456,10 @@
 	libjpeg \
 	libutils \
 	libz \
-	libexpat
+	libexpat \
+	libutils \
+	libEGL \
+	libGLESv2
 
 LOCAL_STATIC_LIBRARIES := \
 	libft2 \
@@ -294,27 +468,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 +508,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 +515,12 @@
 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
+
+#############################################################
+# Build the legacy skia library for playback of saved webpages
+#
+include $(BASE_PATH)/legacy/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 94a90c1..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,163 +148,37 @@
     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 bool filter(SkPaint*, Type) SK_OVERRIDE;
@@ -256,380 +187,908 @@
     if (kText_Type == t) {
         p->setAntiAlias(false);
     }
-    return 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
@@ -645,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;
@@ -767,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);
@@ -780,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/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/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/SkCanvas.h b/include/core/SkCanvas.h
index 76c6c88..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,34 +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 false; }
+    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
@@ -414,12 +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 false; }
+    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
@@ -431,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
@@ -447,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
@@ -590,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
@@ -648,8 +667,6 @@
     virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
                             const SkPaint* paint = NULL);
 
-    virtual void drawBitmapRectToRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*) {}
-
     /** Draw the specified bitmap, with the specified matrix applied (before the
         canvas' matrix is applied).
         @param bitmap   The bitmap to be drawn
@@ -658,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);
@@ -776,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.
     */
@@ -803,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)
@@ -877,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;
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -946,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;
@@ -965,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);
@@ -985,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();
@@ -1007,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:
@@ -1053,6 +1101,8 @@
 #else
     void validateClip() const {}
 #endif
+
+    typedef SkRefCnt INHERITED;
 };
 
 /** Stack helper class to automatically call restoreToCount() on the canvas
@@ -1069,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/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/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/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 9bc7b0d..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 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/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/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 a59b68c..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,33 +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*) const { return 0; }
+    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
@@ -542,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,
@@ -567,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;
@@ -576,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);
     }
 
@@ -609,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);
@@ -621,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);
@@ -633,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 d2233f0..54a4a6d 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,30 +879,94 @@
     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 SkGlyph& getGlyphMetrics(uint16_t);
-    const void* findImage(const SkGlyph&);
+    const SkGlyph& getUnicharMetrics(SkUnichar, const SkMatrix*);
+    const SkGlyph& getGlyphMetrics(uint16_t, const SkMatrix*);
+    const void* findImage(const SkGlyph&, const SkMatrix*);
 
     uint32_t getGenerationID() const;
+    void setGenerationID(uint32_t generationID);
 
     /** 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 +976,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 +1006,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 +1020,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 +1031,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/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/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/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/SkRegion.h b/include/core/SkRegion.h
index a0762df..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,14 +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 { return 0; }
+    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
@@ -379,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/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/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/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/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/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/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 1d0a2fa..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 bool 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/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/SkTypeface_android.h b/include/ports/SkTypeface_android.h
index c3eb3d1..7e9204d 100644
--- a/include/ports/SkTypeface_android.h
+++ b/include/ports/SkTypeface_android.h
@@ -13,6 +13,7 @@
 #include "SkPaint.h"
 
 #include "../harfbuzz/src/harfbuzz-shaper.h"
+#include "../harfbuzz_ng/src/hb.h"
 
 /**
  *  Return a new typeface for a fallback script. If the script is
@@ -23,6 +24,9 @@
  *  @return          reference to the matching typeface. Caller must call
  *                   unref() when they are done.
  */
+SK_API SkTypeface* SkCreateTypefaceForScriptNG(hb_script_t script, SkTypeface::Style style,
+        SkPaint::FontVariant fontVariant = SkPaint::kDefault_Variant);
+
 SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
         SkPaint::FontVariant fontVariant = SkPaint::kDefault_Variant);
 
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/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/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/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/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/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/legacy/Android.mk b/legacy/Android.mk
new file mode 100644
index 0000000..9bb40f1
--- /dev/null
+++ b/legacy/Android.mk
@@ -0,0 +1,313 @@
+BASE_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
+
+###############################################################################
+#
+# This is a older copy of Skia that is intended only to be used to support
+# playback of webpages saved as SkPictures in older versions of the Android
+# Browser.  As such, all symbols are hidden with the exception of a few
+# functions that are needed to enable that behavior.
+#
+###############################################################################
+
+
+#############################################################
+#   build the skia+fretype+png+jpeg+zlib+gif+webp library
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+# need a flag to tell the C side when we're on devices with large memory
+# budgets (i.e. larger than the low-end devices that initially shipped)
+ifeq ($(ARCH_ARM_HAVE_VFP),true)
+    LOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE
+endif
+
+ifeq ($(TARGET_ARCH),x86)
+    LOCAL_CFLAGS += -DANDROID_LARGE_MEMORY_DEVICE
+endif
+
+ifneq ($(ARCH_ARM_HAVE_VFP),true)
+	LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT
+endif
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+	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
+
+
+LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_SRC_FILES:= SavedPagePlayback.cpp
+
+LOCAL_SRC_FILES += \
+	src/core/Sk64.cpp \
+	src/core/SkAAClip.cpp \
+	src/core/SkAdvancedTypefaceMetrics.cpp \
+	src/core/SkAlphaRuns.cpp \
+	src/core/SkBitmap.cpp \
+	src/core/SkBitmapProcShader.cpp \
+	src/core/SkBitmapProcState.cpp \
+	src/core/SkBitmapProcState_matrixProcs.cpp \
+	src/core/SkBitmapSampler.cpp \
+	src/core/SkBitmap_scroll.cpp \
+	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/SkConfig8888.cpp \
+	src/core/SkCordic.cpp \
+	src/core/SkCubicClipper.cpp \
+	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/SkEdgeBuilder.cpp \
+	src/core/SkEdgeClipper.cpp \
+  src/core/SkEdge.cpp \
+	src/core/SkFilterProc.cpp \
+	src/core/SkFlattenable.cpp \
+	src/core/SkFloat.cpp \
+	src/core/SkFloatBits.cpp \
+	src/core/SkFontHost.cpp \
+	src/core/SkGeometry.cpp \
+	src/core/SkGlyphCache.cpp \
+	src/core/SkGraphics.cpp \
+	src/core/SkLanguage.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/SkPathMeasure.cpp \
+	src/core/SkPicture.cpp \
+	src/core/SkPictureFlat.cpp \
+	src/core/SkPicturePlayback.cpp \
+	src/core/SkPictureRecord.cpp \
+	src/core/SkPixelRef.cpp \
+	src/core/SkPoint.cpp \
+	src/core/SkProcSpriteBlitter.cpp \
+	src/core/SkPtrRecorder.cpp \
+	src/core/SkQuadClipper.cpp \
+	src/core/SkRasterClip.cpp \
+	src/core/SkRasterizer.cpp \
+	src/core/SkRect.cpp \
+	src/core/SkRefDict.cpp \
+	src/core/SkRegion.cpp \
+	src/core/SkRegion_path.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/SkStroke.cpp \
+	src/core/SkStrokerPriv.cpp \
+	src/core/SkTSearch.cpp \
+	src/core/SkTypeface.cpp \
+	src/core/SkTypefaceCache.cpp \
+	src/core/SkUnPreMultiply.cpp \
+	src/core/SkUtils.cpp \
+	src/core/SkFlate.cpp \
+	src/core/SkWriter32.cpp \
+	src/core/SkXfermode.cpp \
+	src/effects/Sk1DPathEffect.cpp \
+	src/effects/Sk2DPathEffect.cpp \
+	src/effects/SkAvoidXfermode.cpp \
+	src/effects/SkArithmeticMode.cpp \
+	src/effects/SkBitmapCache.cpp \
+	src/effects/SkBlurDrawLooper.cpp \
+	src/effects/SkBlurImageFilter.cpp \
+	src/effects/SkBlurMask.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/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/SkRectShape.cpp \
+	src/effects/SkTableColorFilter.cpp \
+  src/effects/SkTableMaskFilter.cpp \
+  src/effects/SkTestImageFilters.cpp \
+	src/effects/SkTransparentShader.cpp \
+	src/images/bmpdecoderhelper.cpp \
+	src/images/SkBitmapRegionDecoder.cpp \
+	src/images/SkCreateRLEPixelRef.cpp \
+	src/images/SkFDStream.cpp \
+	src/images/SkFlipPixelRef.cpp \
+	src/images/SkImageDecoder.cpp \
+	src/images/SkImageDecoder_Factory.cpp \
+	src/images/SkImageDecoder_libbmp.cpp \
+	src/images/SkImageDecoder_libgif.cpp \
+	src/images/SkImageDecoder_libico.cpp \
+	src/images/SkImageDecoder_libjpeg.cpp \
+	src/images/SkImageDecoder_libpng.cpp \
+	src/images/SkImageDecoder_libwebp.cpp \
+	src/images/SkImageDecoder_wbmp.cpp \
+	src/images/SkImageEncoder.cpp \
+	src/images/SkImageEncoder_Factory.cpp \
+	src/images/SkImageRef.cpp \
+	src/images/SkImageRefPool.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/ports/FontHostConfiguration_android.cpp \
+	src/ports/SkDebug_android.cpp \
+	src/ports/SkGlobalInitialization_default.cpp \
+	src/ports/SkFontHost_FreeType.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/SkBoundaryPatch.cpp \
+	src/utils/SkCamera.cpp \
+	src/utils/SkColorMatrix.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/SkMeshUtils.cpp \
+	src/utils/SkNinePatch.cpp \
+	src/utils/SkNWayCanvas.cpp \
+	src/utils/SkOSFile.cpp \
+	src/utils/SkParse.cpp \
+	src/utils/SkParseColor.cpp \
+	src/utils/SkParsePath.cpp \
+	src/utils/SkProxyCanvas.cpp \
+	src/utils/SkSfntUtils.cpp \
+	src/utils/SkUnitMappers.cpp
+
+ifeq ($(TARGET_ARCH),arm)
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+LOCAL_SRC_FILES += \
+	src/opts/memset16_neon.S \
+	src/opts/memset32_neon.S
+endif
+
+LOCAL_SRC_FILES += \
+	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 \
+	src/opts/SkBitmapProcState_opts_none.cpp \
+	src/opts/SkUtils_opts_none.cpp
+endif
+
+# these are for emoji support, needed by webkit
+LOCAL_SRC_FILES += \
+	emoji/EmojiFont.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libemoji \
+	libjpeg \
+	libutils \
+	libz \
+	libexpat
+
+LOCAL_STATIC_LIBRARIES := \
+	libft2 \
+	libpng \
+	libgif \
+	libwebp-decode \
+	libwebp-encode
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/src/core \
+	$(LOCAL_PATH)/include/core \
+	$(LOCAL_PATH)/include/config \
+	$(LOCAL_PATH)/include/effects \
+	$(LOCAL_PATH)/include/images \
+	$(LOCAL_PATH)/include/ports \
+	$(LOCAL_PATH)/include/utils \
+	$(LOCAL_PATH)/include/xml \
+	external/freetype/include \
+	external/zlib \
+	external/libpng \
+	external/giflib \
+	external/jpeg \
+	external/webp/include \
+	frameworks/opt/emoji \
+	external/expat/lib
+
+ifeq ($(NO_FALLBACK_FONT),true)
+	LOCAL_CFLAGS += -DNO_FALLBACK_FONT
+endif
+
+LOCAL_LDLIBS += -lpthread
+
+LOCAL_MODULE:= libskia_legacy
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/legacy/SavedPagePlayback.cpp b/legacy/SavedPagePlayback.cpp
new file mode 100644
index 0000000..f5cdd40
--- /dev/null
+++ b/legacy/SavedPagePlayback.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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.
+ */
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+#include "SkPicture.h"
+#include "SkRegion.h"
+#include "SkStream.h"
+
+#define EXPORT_FUNC extern "C" __attribute__ ((visibility ("default")))
+
+EXPORT_FUNC int legacy_skia_create_picture(const void* pictureStream, int streamLength,
+                                           void**legacyPicture, int* width, int* height) {
+    SkMemoryStream stream(pictureStream, streamLength);
+    SkPicture* picture = new SkPicture(&stream);
+    *legacyPicture = picture;
+    *width = picture->width();
+    *height = picture->height();
+    return stream.peek();
+}
+
+EXPORT_FUNC void legacy_skia_delete_picture(void* legacyPicture) {
+  free(legacyPicture);
+}
+
+EXPORT_FUNC void legacy_skia_draw_picture(void* legacyPicture, void* matrixStorage,
+                                          void* clipStorage, int bitmapWidth,
+                                          int bitmapHeight, int bitmapConfig,
+                                          int bitmapRowBytes, void* pixels) {
+    SkMatrix matrix;
+    matrix.unflatten(matrixStorage);
+
+    SkRegion region;
+    region.unflatten(clipStorage);
+
+    SkBitmap bitmap;
+    bitmap.setConfig((SkBitmap::Config)bitmapConfig, bitmapWidth, bitmapHeight, bitmapRowBytes);
+    bitmap.setPixels(pixels);
+
+    SkCanvas canvas(bitmap);
+    canvas.setMatrix(matrix);
+    canvas.setClipRegion(region);
+    canvas.drawPicture(*(SkPicture*)legacyPicture);
+}
diff --git a/legacy/emoji/EmojiFont.cpp b/legacy/emoji/EmojiFont.cpp
new file mode 100644
index 0000000..9436f26
--- /dev/null
+++ b/legacy/emoji/EmojiFont.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "EmojiFactory.h"
+#include "EmojiFont.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkPaint.h"
+#include "SkTSearch.h"
+#include "SkUtils.h"
+
+#include "gmoji_pua_table.h"
+
+#include <string.h>
+
+namespace android {
+
+// lazily allocate the factory
+static EmojiFactory* get_emoji_factory() {
+    static EmojiFactory* gEmojiFactory;
+    if (NULL == gEmojiFactory) {
+        gEmojiFactory = EmojiFactory::GetAvailableImplementation();
+        // we may still be NULL, if there is no impl.
+    }
+    return gEmojiFactory;
+}
+
+#define UNINITIALIZED_ENCODE_SIZE   0   // our array is initialzed with 0s
+#define NOT_AVAILABLE_ENCODE_SIZE   -1  // never a legal length for data
+
+struct EncodeDataRec {
+    SkBitmap*   fBitmap;
+    const void* fData;
+    int         fSize;
+};
+
+static EncodeDataRec gGmojiEncodeData[GMOJI_PUA_COUNT] = {};
+
+/*  Given a local index, return (initialized if needed) a rec containing the
+    encoded data and length. The bitmap field is initialized to 0, and is not
+    filled in by this routine per-se.
+ */
+static EncodeDataRec* get_encoderec(int index) {
+    if ((unsigned)index >= GMOJI_PUA_COUNT) {
+        SkDebugf("bad index passed to EncodeDataRec& get_encode_data %d\n",
+                 index);
+        return NULL;
+    }
+
+    // lazily fill in the data
+    EncodeDataRec* rec = &gGmojiEncodeData[index];
+
+    if (NOT_AVAILABLE_ENCODE_SIZE == rec->fSize) {
+        return NULL;
+    }
+    if (UNINITIALIZED_ENCODE_SIZE == rec->fSize) {
+        EmojiFactory* fact = get_emoji_factory();
+        if (NULL == fact) {
+            return NULL;
+        }
+
+        int32_t pua = GMOJI_PUA_MIN + gGmojiPUA[index];
+        rec->fData = fact->GetImageBinaryFromAndroidPua(pua, &rec->fSize);
+        if (NULL == rec->fData) {
+            // flag this entry is not available, so we won't ask again
+            rec->fSize = NOT_AVAILABLE_ENCODE_SIZE;
+            return NULL;
+        }
+    }
+    return rec;
+}
+
+/*  Return the bitmap associated with the local index, or NULL if none is
+    available. Note that this will try to cache the bitmap the first time it
+    creates it.
+ */
+static const SkBitmap* get_bitmap(int index) {
+    EncodeDataRec* rec = get_encoderec(index);
+    SkBitmap* bitmap = NULL;
+    if (rec) {
+        bitmap = rec->fBitmap;
+        if (NULL == bitmap) {
+            bitmap = new SkBitmap;
+            if (!SkImageDecoder::DecodeMemory(rec->fData, rec->fSize, bitmap)) {
+                delete bitmap;
+                // we failed, so mark us to not try again
+                rec->fSize = NOT_AVAILABLE_ENCODE_SIZE;
+                return NULL;
+            }
+            // cache the answer
+            rec->fBitmap = bitmap;
+            // todo: we never know if/when to let go of this cached bitmap
+            // tho, since the pixels are managed separately, and are purged,
+            // the "leak" may not be too important
+        }
+    }
+    return bitmap;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool EmojiFont::IsAvailable() {
+    return get_emoji_factory() != NULL;
+}
+
+const char *EmojiFont::GetShiftJisConverterName() {
+    EmojiFactory* fact = get_emoji_factory();
+    if (NULL != fact) {
+        if (strcmp(fact->Name(), "kddi") == 0) {
+            return "kddi-emoji";
+        } else if (strcmp(fact->Name(), "softbank") == 0) {
+            return "softbank-emoji";
+        }
+    }
+
+    // Until Eclair, we have used DoCoMo's Shift_JIS table.
+    return "docomo-emoji";
+}
+
+uint16_t EmojiFont::UnicharToGlyph(int32_t unichar) {
+    // do a quick range check before calling the search routine
+    if (unichar >= GMOJI_PUA_MIN && unichar <= GMOJI_PUA_MAX) {
+        // our table is stored relative to GMOJI_PUA_MIN to save space (16bits)
+        uint16_t relative = unichar - GMOJI_PUA_MIN;
+        int index = SkTSearch<uint16_t>(gGmojiPUA, GMOJI_PUA_COUNT, relative,
+                                        sizeof(uint16_t));
+        // a negative value means it was not found
+        if (index >= 0) {
+            return index + kGlyphBase;
+        }
+        // fall through to return 0
+    }
+    // not a supported emoji pua
+    return 0;
+}
+
+SkScalar EmojiFont::GetAdvanceWidth(uint16_t glyphID, const SkPaint& paint) {
+    if (glyphID < kGlyphBase) {
+        SkDebugf("-------- bad glyph passed to EmojiFont::GetAdvanceWidth %d\n",
+                 glyphID);
+        return 0;
+    }
+
+    const SkBitmap* bitmap = get_bitmap(glyphID - kGlyphBase);
+    if (NULL == bitmap) {
+        return 0;
+    }
+
+    // assume that our advance width is always the pointsize
+    return paint.getTextSize();
+}
+
+/*  This tells us to shift the emoji bounds down by 20% below the baseline,
+    to better align with the Kanji characters' placement in the line.
+ */
+static const SkScalar gBaselinePercentDrop = SkFloatToScalar(0.2f);
+    
+void EmojiFont::Draw(SkCanvas* canvas, uint16_t glyphID,
+                     SkScalar x, SkScalar y, const SkPaint& paint) {
+    if (glyphID < kGlyphBase) {
+        SkDebugf("-------- bad glyph passed to EmojiFont::Draw %d\n", glyphID);
+    }
+
+    const SkBitmap* bitmap = get_bitmap(glyphID - kGlyphBase);
+    if (bitmap && !bitmap->empty()) {
+        SkRect dst;
+        SkScalar size = paint.getTextSize();
+        y += SkScalarMul(size, gBaselinePercentDrop);
+        dst.set(x, y - size, x + size, y);
+        canvas->drawBitmapRect(*bitmap, NULL, dst, &paint);
+    }
+}
+
+}
diff --git a/legacy/emoji/EmojiFont.h b/legacy/emoji/EmojiFont.h
new file mode 100644
index 0000000..a452bca
--- /dev/null
+++ b/legacy/emoji/EmojiFont.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * 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 android_EmojiFont_DEFINED
+#define android_EmojiFont_DEFINED
+
+#include "SkScalar.h"
+#include "SkUtils.h"
+
+class SkCanvas;
+class SkPaint;
+
+namespace android {
+
+    class EmojiFont {
+    public:
+        /** Returns true if the underlying emoji font mechanism is available.
+         */
+        static bool IsAvailable();
+
+        /** Returns index for the corresponding index to the emoji table, or 0
+            if there is no matching emoji form.
+         */
+        static uint16_t UnicharToGlyph(int32_t unichar);
+
+        /** Returns true if the specified glyph is in the emoji range, i.e. was
+            returned by UnicharToGlyph or UTF16ToGlyph.
+         */
+        static bool IsEmojiGlyph(uint16_t index) {
+            return index >= kGlyphBase;
+        }
+
+        /** Returns the advance width for the specified emoji form.
+         */
+        static SkScalar GetAdvanceWidth(uint16_t index, const SkPaint& paint);
+
+        /** Draw the specified emoji form, given the x,y origin of the text
+            version. The paint is the one associated with the text that has
+            the emoji in it.
+         */
+        static void Draw(SkCanvas*, uint16_t index, SkScalar x, SkScalar y,
+                         const SkPaint& paint);
+
+        /** Returns the conver name for Shift_JIS (one of Japanese charset)
+         */
+        static const char* GetShiftJisConverterName();
+    private:
+        enum {
+            /*  this is our internal trick to embedded private emoji glyph IDs
+                along side normal glyphs IDs that come from real fonts. The
+                assumption is that normal fonts never will report a glyph ID
+                above 20K or 30K, so 64000 should always be a safe starting
+                index. We also assume the the number of emoji will not overflow
+                16bits starting at 64000 i.e. 65535 - 64000 > total emoji count
+             */
+            kGlyphBase = 64000
+        };
+    };
+}
+
+#endif
diff --git a/legacy/emoji/GmojiMaker.cpp b/legacy/emoji/GmojiMaker.cpp
new file mode 100644
index 0000000..6115d91
--- /dev/null
+++ b/legacy/emoji/GmojiMaker.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+static const char gSite[] = "http://www.corp.google.com/eng/doc/emoji/dev.html";
+
+using namespace std;
+
+static int hexchar_to_int(char c) {
+    if (c >= '0' && c <= '9') {
+        return c - '0';
+    }
+    if (c >= 'A' && c <= 'F') {
+        return 10 + c - 'A';
+    }
+    if (c >= 'a' && c <= 'f') {
+        return 10 + c - 'a';
+    }
+    return -1;  // unrecognized char for nex
+}
+
+/*  Tool to build gmoji_pua table, listing all of the pua values for gmoji
+ */
+int main (int argc, char * const argv[]) {
+    
+    char buffer[10000];    
+    FILE* file = fopen(argv[1], "r");
+    if (NULL == file) {
+        std::cerr << "Can't open " << argv[1] << " for input. Aborting\n";
+        std::cout << "\n";
+        return -1;
+    }
+    
+    vector<int> unichars;
+    int lineNo = 0;
+    for (;;) {
+        if (fgets(buffer, sizeof(buffer), file) == 0) {
+            break;
+        }
+        
+        int prevPua = 0;
+        int pua = 0;
+        // we just want to eat the first 5 chars
+        for (int i = 0; i < 5; i++) {
+            int value = hexchar_to_int(buffer[i]);
+            if (value < 0) {    // bad char for hex
+                std::cerr << "Expected hex char on line " << lineNo
+                          << " col " << i << "\n";
+                return -1;
+            }
+            pua = (pua << 4) | value;
+        }
+        if (pua < 0xFE000 || pua > 0xFEFFF) {
+            std::cerr << "PUA not in expected range " << pua << " line "
+                      << lineNo << "\n";
+            return -1;
+        }
+        if (pua <= prevPua) {
+            std::cerr << "PUA value not in ascending order line "
+                      << lineNo << "\n";
+            return -1;
+        }
+        unichars.push_back(pua);
+        prevPua = pua;
+        lineNo++;
+    }
+    
+    // Now output our resulting array to look like a C array
+    const int perLine = 8;
+    const int base = unichars[0];
+    printf("\n");
+    printf("// Compressed gmoji table, sorted\n");
+    printf("// Originally scraped from %s\n", gSite);
+    printf("// Input text file \"%s\"\n", argv[1]);
+    printf("\n");
+    printf("static const uint16_t gGmojiPUA[] = {\n");
+    for (int i = 0; i < unichars.size(); i++) {
+        if ((i % perLine) == 0) {   // first one
+            printf("    ");
+        }
+        printf("0x%03X", unichars[i] - base);
+        if (i == unichars.size() - 1) { // last one entirely
+            printf("\n");
+        }
+        else if ((i % perLine) == (perLine - 1)) {   // last one on line
+            printf(",\n");
+        } else {
+            printf(", ");
+        }
+    }
+    printf("};\n");
+    printf("\n");
+    printf("#define GMOJI_PUA_MIN   0x%X\n", unichars[0]);
+    printf("#define GMOJI_PUA_MAX   0x%X\n", unichars[unichars.size()-1]);
+    printf("#define GMOJI_PUA_COUNT (sizeof(gGmojiPUA) / sizeof(gGmojiPUA[0]))\n");
+    printf("// GMOJI_PUA_COUNT should be %d\n", unichars.size());
+    printf("\n");
+    
+    fclose(file);
+    return 0;
+}
diff --git a/legacy/emoji/gmoji_pua_table.h b/legacy/emoji/gmoji_pua_table.h
new file mode 100644
index 0000000..3f68163
--- /dev/null
+++ b/legacy/emoji/gmoji_pua_table.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+
+// Compressed gmoji table, sorted
+// Originally scraped from http://www.corp.google.com/eng/doc/emoji/dev.html
+// Input text file "external/webkit/WebKit/android/emoji/gmojiraw.txt"
+
+static const uint16_t gGmojiPUA[] = {
+    0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007,
+    0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F,
+    0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017,
+    0x018, 0x019, 0x01A, 0x01B, 0x01C, 0x01D, 0x01E, 0x01F,
+    0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027,
+    0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F,
+    0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037,
+    0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F,
+    0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047,
+    0x048, 0x049, 0x04A, 0x04B, 0x04C, 0x04D, 0x04E, 0x04F,
+    0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057,
+    0x058, 0x059, 0x05A, 0x05B, 0x190, 0x191, 0x192, 0x193,
+    0x194, 0x195, 0x196, 0x197, 0x198, 0x199, 0x19A, 0x19B,
+    0x19C, 0x19D, 0x19E, 0x19F, 0x1A0, 0x1A1, 0x1A2, 0x1A3,
+    0x1A4, 0x1A5, 0x1A6, 0x1A7, 0x1A8, 0x1A9, 0x1AA, 0x1AB,
+    0x1AC, 0x1AD, 0x1AE, 0x1AF, 0x1B0, 0x1B1, 0x1B2, 0x1B3,
+    0x1B4, 0x1B5, 0x1B6, 0x1B7, 0x1B8, 0x1B9, 0x1BA, 0x1BB,
+    0x1BC, 0x1BD, 0x1BE, 0x1BF, 0x1C0, 0x1C1, 0x1C2, 0x1C3,
+    0x1C4, 0x1C5, 0x1C6, 0x1C7, 0x1C8, 0x1C9, 0x1CA, 0x1CB,
+    0x1CC, 0x1CD, 0x1CE, 0x1CF, 0x1D0, 0x1D1, 0x1D2, 0x1D3,
+    0x1D4, 0x1D5, 0x1D6, 0x1D7, 0x1D8, 0x1D9, 0x1DA, 0x1DB,
+    0x1DC, 0x1DD, 0x1DE, 0x1DF, 0x1E0, 0x1E1, 0x1E2, 0x1E3,
+    0x320, 0x321, 0x322, 0x323, 0x324, 0x325, 0x326, 0x327,
+    0x328, 0x329, 0x32A, 0x32B, 0x32C, 0x32D, 0x32E, 0x32F,
+    0x330, 0x331, 0x332, 0x333, 0x334, 0x335, 0x336, 0x337,
+    0x338, 0x339, 0x33A, 0x33B, 0x33C, 0x33D, 0x33E, 0x33F,
+    0x340, 0x341, 0x342, 0x343, 0x344, 0x345, 0x346, 0x347,
+    0x348, 0x349, 0x34A, 0x34B, 0x34C, 0x34D, 0x34E, 0x34F,
+    0x350, 0x351, 0x352, 0x353, 0x354, 0x355, 0x356, 0x357,
+    0x358, 0x359, 0x35A, 0x35B, 0x35C, 0x35D, 0x35E, 0x35F,
+    0x360, 0x361, 0x362, 0x363, 0x364, 0x365, 0x366, 0x367,
+    0x368, 0x369, 0x4B0, 0x4B1, 0x4B2, 0x4B3, 0x4B4, 0x4B5,
+    0x4B6, 0x4B7, 0x4B8, 0x4B9, 0x4BA, 0x4BB, 0x4BC, 0x4BD,
+    0x4BE, 0x4BF, 0x4C0, 0x4C1, 0x4C2, 0x4C3, 0x4C4, 0x4C5,
+    0x4C6, 0x4C7, 0x4C8, 0x4C9, 0x4CA, 0x4CB, 0x4CC, 0x4CD,
+    0x4CE, 0x4CF, 0x4D0, 0x4D1, 0x4D2, 0x4D3, 0x4D4, 0x4D5,
+    0x4D6, 0x4D7, 0x4D8, 0x4D9, 0x4DA, 0x4DB, 0x4DC, 0x4DD,
+    0x4DE, 0x4DF, 0x4E0, 0x4E1, 0x4E2, 0x4E3, 0x4E4, 0x4E5,
+    0x4E6, 0x4E7, 0x4E8, 0x4E9, 0x4EA, 0x4EB, 0x4EC, 0x4ED,
+    0x4EE, 0x4EF, 0x4F0, 0x4F1, 0x4F2, 0x4F3, 0x4F4, 0x4F5,
+    0x4F6, 0x4F7, 0x4F8, 0x4F9, 0x4FA, 0x4FB, 0x4FC, 0x4FD,
+    0x4FE, 0x4FF, 0x500, 0x501, 0x502, 0x503, 0x504, 0x505,
+    0x506, 0x507, 0x508, 0x509, 0x50A, 0x50B, 0x50C, 0x50D,
+    0x50E, 0x50F, 0x510, 0x511, 0x512, 0x513, 0x514, 0x515,
+    0x516, 0x517, 0x518, 0x519, 0x51A, 0x51B, 0x51C, 0x51D,
+    0x51E, 0x51F, 0x520, 0x521, 0x522, 0x523, 0x524, 0x525,
+    0x526, 0x527, 0x528, 0x529, 0x52A, 0x52B, 0x52C, 0x52D,
+    0x52E, 0x52F, 0x530, 0x531, 0x532, 0x533, 0x534, 0x535,
+    0x536, 0x537, 0x538, 0x539, 0x53A, 0x53B, 0x53C, 0x53D,
+    0x53E, 0x53F, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545,
+    0x546, 0x547, 0x548, 0x549, 0x54A, 0x54B, 0x54C, 0x54D,
+    0x54E, 0x54F, 0x550, 0x551, 0x552, 0x553, 0x7D0, 0x7D1,
+    0x7D2, 0x7D3, 0x7D4, 0x7D5, 0x7D6, 0x7D7, 0x7D8, 0x7D9,
+    0x7DA, 0x7DB, 0x7DC, 0x7DD, 0x7DE, 0x7DF, 0x7E0, 0x7E1,
+    0x7E2, 0x7E3, 0x7E4, 0x7E5, 0x7E6, 0x7E7, 0x7E8, 0x7E9,
+    0x7EA, 0x7EB, 0x7EC, 0x7ED, 0x7EE, 0x7EF, 0x7F0, 0x7F1,
+    0x7F2, 0x7F3, 0x7F4, 0x7F5, 0x7F6, 0x7F7, 0x7F8, 0x7F9,
+    0x7FA, 0x7FB, 0x7FC, 0x7FD, 0x7FE, 0x7FF, 0x800, 0x801,
+    0x802, 0x803, 0x804, 0x805, 0x806, 0x807, 0x808, 0x809,
+    0x80A, 0x80B, 0x80C, 0x80D, 0x80E, 0x80F, 0x810, 0x811,
+    0x812, 0x813, 0x814, 0x815, 0x816, 0x817, 0x818, 0x819,
+    0x81A, 0x81B, 0x81C, 0x81D, 0x81E, 0x81F, 0x820, 0x821,
+    0x822, 0x823, 0x824, 0x825, 0x826, 0x827, 0x828, 0x829,
+    0x82A, 0x82B, 0x82C, 0x82D, 0x82E, 0x82F, 0x830, 0x831,
+    0x832, 0x833, 0x834, 0x835, 0x836, 0x837, 0x838, 0x839,
+    0x83A, 0x83B, 0x83C, 0x960, 0x961, 0x962, 0x963, 0x964,
+    0x965, 0x966, 0x967, 0x968, 0x969, 0x96A, 0x96B, 0x96C,
+    0x96D, 0x96E, 0x96F, 0x970, 0x971, 0x972, 0x973, 0x974,
+    0x975, 0x976, 0x977, 0x978, 0x979, 0x97A, 0x97B, 0x97C,
+    0x97D, 0x97E, 0x97F, 0x980, 0x981, 0x982, 0x983, 0x984,
+    0x985, 0x986, 0x987, 0x988, 0xAF0, 0xAF1, 0xAF2, 0xAF3,
+    0xAF4, 0xAF5, 0xAF6, 0xAF7, 0xAF8, 0xAF9, 0xAFA, 0xAFB,
+    0xAFC, 0xAFD, 0xAFE, 0xAFF, 0xB00, 0xB01, 0xB02, 0xB03,
+    0xB04, 0xB05, 0xB06, 0xB07, 0xB08, 0xB09, 0xB0A, 0xB0B,
+    0xB0C, 0xB0D, 0xB0E, 0xB0F, 0xB10, 0xB11, 0xB12, 0xB13,
+    0xB14, 0xB15, 0xB16, 0xB17, 0xB18, 0xB19, 0xB1A, 0xB1B,
+    0xB1C, 0xB1D, 0xB1E, 0xB1F, 0xB20, 0xB21, 0xB22, 0xB23,
+    0xB24, 0xB25, 0xB26, 0xB27, 0xB28, 0xB29, 0xB2A, 0xB2B,
+    0xB2C, 0xB2D, 0xB2E, 0xB2F, 0xB30, 0xB31, 0xB32, 0xB33,
+    0xB34, 0xB35, 0xB36, 0xB37, 0xB38, 0xB39, 0xB3A, 0xB3B,
+    0xB3C, 0xB3D, 0xB3E, 0xB3F, 0xB40, 0xB41, 0xB42, 0xB43,
+    0xB44, 0xB45, 0xB46, 0xB47, 0xB48, 0xB49, 0xB4A, 0xB4B,
+    0xB4C, 0xB4D, 0xB4E, 0xB4F, 0xB50, 0xB51, 0xB52, 0xB53,
+    0xB54, 0xB55, 0xB56, 0xB57, 0xB58, 0xB59, 0xB5A, 0xB5B,
+    0xB5C, 0xB5D, 0xB5E, 0xB5F, 0xB60, 0xB61, 0xB62, 0xB63,
+    0xB64, 0xB65, 0xB66, 0xB67, 0xB68, 0xB69, 0xB6A, 0xB6B,
+    0xB6C, 0xB6D, 0xB6E, 0xB6F, 0xB70, 0xB71, 0xB72, 0xB73,
+    0xB74, 0xB75, 0xB76, 0xB77, 0xB78, 0xB79, 0xB7A, 0xB7B,
+    0xB7C, 0xB7D, 0xB7E, 0xB7F, 0xB80, 0xB81, 0xB82, 0xB83,
+    0xB84, 0xB85, 0xB86, 0xB87, 0xB88, 0xB89, 0xB8A, 0xB8B,
+    0xB8C, 0xB8D, 0xB8E, 0xB8F, 0xB90, 0xB91, 0xB92, 0xB93,
+    0xB94, 0xB95, 0xB96, 0xB97, 0xB98, 0xB99, 0xB9A, 0xB9B,
+    0xB9C, 0xB9D, 0xB9E, 0xB9F, 0xBA0, 0xBA1, 0xBA2, 0xE10,
+    0xE11, 0xE12, 0xE13, 0xE14, 0xE15, 0xE16, 0xE17, 0xE18,
+    0xE19, 0xE1A, 0xE1B, 0xE1C, 0xE1D, 0xE1E, 0xE1F, 0xE20,
+    0xE21, 0xE22, 0xE23, 0xE24, 0xE25, 0xE26, 0xE27, 0xE28,
+    0xE29, 0xE2A, 0xE2B, 0xE2C, 0xE2D, 0xE2E, 0xE2F, 0xE30,
+    0xE31, 0xE32, 0xE33, 0xE40, 0xE41, 0xE42, 0xE43, 0xE44,
+    0xE45, 0xE46, 0xE47, 0xE48, 0xE49, 0xE4A, 0xE70, 0xE71,
+    0xE72, 0xE73, 0xE74, 0xE75, 0xE76, 0xE77, 0xE78, 0xE79,
+    0xE7A, 0xE7B, 0xE7C, 0xE7D, 0xEA0
+};
+
+#define GMOJI_PUA_MIN   0xFE000
+#define GMOJI_PUA_MAX   0xFEEA0
+#define GMOJI_PUA_COUNT (sizeof(gGmojiPUA) / sizeof(gGmojiPUA[0]))
+// GMOJI_PUA_COUNT should be 805
+
diff --git a/legacy/emoji/gmojiraw.txt b/legacy/emoji/gmojiraw.txt
new file mode 100644
index 0000000..33ad18d
--- /dev/null
+++ b/legacy/emoji/gmojiraw.txt
@@ -0,0 +1,805 @@
+FE000	\uDBB8\uDC00	\xF3\xBE\x80\x80	BLACK SUN WITH RAYS		44	F660	E488	7541	EB60		1	F89F	E63E	7541		74	F98B	E04A	7541			
+FE001	\uDBB8\uDC01	\xF3\xBE\x80\x81	CLOUD		107	F665	E48D	7546	EB65		2	F8A0	E63F	7546		73	F98A	E049	7546			
+FE002	\uDBB8\uDC02	\xF3\xBE\x80\x82	UMBRELLA WITH RAIN DROPS		95	F664	E48C	7545	EB64		3	F8A1	E640	7545		75	F98C	E04B	7545			
+FE003	\uDBB8\uDC03	\xF3\xBE\x80\x83	SNOWMAN		191	F65D	E485	753E	EB5D		4	F8A2	E641	753E		72	F989	E048	753E			
+FE004	\uDBB8\uDC04	\xF3\xBE\x80\x84	LIGHTNING SYMBOL		16	F65F	E487	7540	EB5F		5	F8A3	E642	7540		151	F77D	E13D	7540	 		
+FE005	\uDBB8\uDC05	\xF3\xBE\x80\x85	CYCLONE		190	F641	E469	7522	EB41		6	F8A4	E643	7522		414	FB84	E443	7522	 	 	
+FE006	\uDBB8\uDC06	\xF3\xBE\x80\x86	FOG		305	F7B5	E598	7837	ECB5		7	F8A5	E644	7837	[霧]	 	 	
+FE007	\uDBB8\uDC07	\xF3\xBE\x80\x87	CLOSED UMBRELLA		481	F3BC	EAE8	7A3E	EDBC		8	F8A6	E645	7A3E		407	FB7C	E43C	7A3E	 	 	
+FE008	\uDBB8\uDC08	\xF3\xBE\x80\x88	NIGHT WITH STARS		490	F3C5	EAF1	7A47	EDC5		172	F957	E6B3	7A47		422	FB8C	E44B	7A47	 	 	
+FE009	\uDBB8\uDC09	\xF3\xBE\x80\x89	SUNRISE OVER MOUNTAINS		493	F3C8	EAF4	7A4A	EDC8		1	F89F	E63E	7541		77	F98E	E04D	7D53	 	 	 
+FE00A	\uDBB8\uDC0A	\xF3\xBE\x80\x8A	SUNRISE		493	F3C8	EAF4	7A4A	EDC8		1	F89F	E63E	7541		420	FB8A	E449	7A4A	 	 	 
+FE00B	\uDBB8\uDC0B	\xF3\xBE\x80\x8B	CITYSCAPE AT DUSK		371	F34D	E5DA	792E	ED4D	[夕焼け]		160	F787	E146	792E	 	 	 
+FE00C	\uDBB8\uDC0C	\xF3\xBE\x80\x8C	SUNSET OVER BUILDINGS		371	F34D	E5DA	792E	ED4D		1	F89F	E63E	7541		421	FB8B	E44A	7D4E	 	 	 
+FE00D	\uDBB8\uDC0D	\xF3\xBE\x80\x8D	RAINBOW		491	F3C6	EAF2	7A48	EDC6	[虹]		423	FB8D	E44C	7A48	 	 	 
+FE00E	\uDBB8\uDC0E	\xF3\xBE\x80\x8E	SNOWFLAKE		60	F662	E48A	7543	EB62	[雪結晶]	[雪結晶]	 		 
+FE00F	\uDBB8\uDC0F	\xF3\xBE\x80\x8F	PARTLY CLOUDY		167	F666	E48E	7547	EB66		1+2	F89F+F8A0	E63E+E63F	7541+7546		74+73	F98B+F98A	E04A+E049	7541+7546	 	 	 
+FE010	\uDBB8\uDC10	\xF3\xBE\x80\x90	BRIDGE AT NIGHT		227	F698	E4BF	7578	EB98		172	F957	E6B3	7A47		422	FB8C	E44B	7A47	 	 	 
+FE011	\uDBB8\uDC11	\xF3\xBE\x80\x91	NEW MOON		321	F7C5	E5A8	7847	ECC5		95	F940	E69C	7847	●	 	 	
+FE012	\uDBB8\uDC12	\xF3\xBE\x80\x92	WAXING MOON		322	F7C6	E5A9	7848	ECC6		96	F941	E69D	7848		76	F98D	E04C	753F	 	 	
+FE013	\uDBB8\uDC13	\xF3\xBE\x80\x93	HALF MOON		323	F7C7	E5AA	7849	ECC7		97	F942	E69E	7849		76	F98D	E04C	753F	 	 	
+FE014	\uDBB8\uDC14	\xF3\xBE\x80\x94	CRESCENT MOON		15	F65E	E486	753F	EB5E		98	F943	E69F	753F		76	F98D	E04C	753F		 	
+FE015	\uDBB8\uDC15	\xF3\xBE\x80\x95	FULL MOON	○		99	F944	E6A0	7E24	○	 	 	
+FE016	\uDBB8\uDC16	\xF3\xBE\x80\x96	HALF MOON WITH FACE		47	F661	E489	7542	EB61		97	F942	E69E	7849		76	F98D	E04C	753F	 	 	 
+FE017	\uDBB8\uDC17	\xF3\xBE\x80\x97	MOON OVER A HOUSE		488	F3C3	EAEF	7A45	EDC3	[お月見]		417	FB87	E446	7A45	 	 	 
+FE018	\uDBB8\uDC18	\xF3\xBE\x80\x98	SOON WITH RIGHT ARROW ABOVE	[SOON]		173	F95B	E6B7	7E2D	[SOON]	 	 	
+FE019	\uDBB8\uDC19	\xF3\xBE\x80\x99	ON WITH DOUBLE POINTING ARROW ABOVE	[ON]		174	F95C	E6B8	7E2E	[ON]	 	 	
+FE01A	\uDBB8\uDC1A	\xF3\xBE\x80\x9A	END WITH LEFT ARROW ABOVE	[end]		175	F95D	E6B9	7E2F	[end]	 	 	
+FE01B	\uDBB8\uDC1B	\xF3\xBE\x80\x9B	HOURGLASS		58	F654	E47C	7535	EB54		317	F9C1	E71C	7535	[砂時計]	 	 	
+FE01C	\uDBB8\uDC1C	\xF3\xBE\x80\x9C	HOURGLASS 2		57	F798	E57B	7778	EC98		317	F9C1	E71C	7535	[砂時計]	 	 	 
+FE01D	\uDBB8\uDC1D	\xF3\xBE\x80\x9D	WATCH		25	F797	E57A	7777	EC97		320	F9C4	E71F	7777	[腕時計]	 	 	
+FE01E	\uDBB8\uDC1E	\xF3\xBE\x80\x9E	1 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		36	F964	E024	7D26	 	 	 
+FE01F	\uDBB8\uDC1F	\xF3\xBE\x80\x9F	2 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		37	F965	E025	7D27	 	 	 
+FE020	\uDBB8\uDC20	\xF3\xBE\x80\xA0	3 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		38	F966	E026	7D28	 	 	 
+FE021	\uDBB8\uDC21	\xF3\xBE\x80\xA1	4 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		39	F967	E027	7D29	 	 	 
+FE022	\uDBB8\uDC22	\xF3\xBE\x80\xA2	5 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		40	F968	E028	7D2A	 	 	 
+FE023	\uDBB8\uDC23	\xF3\xBE\x80\xA3	6 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		41	F969	E029	7D2B	 	 	 
+FE024	\uDBB8\uDC24	\xF3\xBE\x80\xA4	7 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		42	F96A	E02A	7D2C	 	 	 
+FE025	\uDBB8\uDC25	\xF3\xBE\x80\xA5	8 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		43	F96B	E02B	7D2D	 	 	 
+FE026	\uDBB8\uDC26	\xF3\xBE\x80\xA6	9 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		44	F96C	E02C	7D2E	 	 	 
+FE027	\uDBB8\uDC27	\xF3\xBE\x80\xA7	10 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		45	F96D	E02D	7D2F	 	 	 
+FE028	\uDBB8\uDC28	\xF3\xBE\x80\xA8	11 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		46	F96E	E02E	7D30	 	 	 
+FE029	\uDBB8\uDC29	\xF3\xBE\x80\xA9	12 OCLOCK		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		47	F96F	E02F	7D31	 	 	 
+FE02A	\uDBB8\uDC2A	\xF3\xBE\x80\xAA	CLOCK SYMBOL		46	F7B1	E594	7833	ECB1		176	F95E	E6BA	7833		45	F96D	E02D	7D2F	 	 	
+FE02B	\uDBB8\uDC2B	\xF3\xBE\x80\xAB	ARIES		192	F667	E48F	7548	EB67		9	F8A7	E646	7548		243	F7DF	E23F	7548	 	 	
+FE02C	\uDBB8\uDC2C	\xF3\xBE\x80\xAC	TAURUS		193	F668	E490	7549	EB68		10	F8A8	E647	7549		244	F7E0	E240	7549	 	 	
+FE02D	\uDBB8\uDC2D	\xF3\xBE\x80\xAD	GEMINI		194	F669	E491	754A	EB69		11	F8A9	E648	754A		245	F7E1	E241	754A	 	 	
+FE02E	\uDBB8\uDC2E	\xF3\xBE\x80\xAE	CANCER		195	F66A	E492	754B	EB6A		12	F8AA	E649	754B		246	F7E2	E242	754B	 	 	
+FE02F	\uDBB8\uDC2F	\xF3\xBE\x80\xAF	LEO		196	F66B	E493	754C	EB6B		13	F8AB	E64A	754C		247	F7E3	E243	754C	 	 	
+FE030	\uDBB8\uDC30	\xF3\xBE\x80\xB0	VIRGO		197	F66C	E494	754D	EB6C		14	F8AC	E64B	754D		248	F7E4	E244	754D	 	 	
+FE031	\uDBB8\uDC31	\xF3\xBE\x80\xB1	LIBRA		198	F66D	E495	754E	EB6D		15	F8AD	E64C	754E		249	F7E5	E245	754E	 	 	
+FE032	\uDBB8\uDC32	\xF3\xBE\x80\xB2	SCORPIO		199	F66E	E496	754F	EB6E		16	F8AE	E64D	754F		250	F7E6	E246	754F	 	 	
+FE033	\uDBB8\uDC33	\xF3\xBE\x80\xB3	SAGITTARIUS		200	F66F	E497	7550	EB6F		17	F8AF	E64E	7550		251	F7E7	E247	7550	 	 	
+FE034	\uDBB8\uDC34	\xF3\xBE\x80\xB4	CAPRICORN		201	F670	E498	7551	EB70		18	F8B0	E64F	7551		252	F7E8	E248	7551	 	 	
+FE035	\uDBB8\uDC35	\xF3\xBE\x80\xB5	AQUARIUS		202	F671	E499	7552	EB71		19	F8B1	E650	7552		253	F7E9	E249	7552	 	 	
+FE036	\uDBB8\uDC36	\xF3\xBE\x80\xB6	PISCES		203	F672	E49A	7553	EB72		20	F8B2	E651	7553		254	F7EA	E24A	7553	 	 	
+FE037	\uDBB8\uDC37	\xF3\xBE\x80\xB7	OPHIUCUS		204	F673	E49B	7554	EB73	[蛇使座]		255	F7EB	E24B	7554	 	 	 
+FE038	\uDBB8\uDC38	\xF3\xBE\x80\xB8	WATER WAVE		810	F481	EB7C	7B61	EE81		352	F9E4	E73F	7B61		409	FB7E	E43E	7B61	 	 	
+FE039	\uDBB8\uDC39	\xF3\xBE\x80\xB9	GREEN EARTH		332	F7D0	E5B3	7852	ECD0	[地球]	[地球]	 	 	 
+FE03A	\uDBB8\uDC3A	\xF3\xBE\x80\xBA	VOLCANO		769	F457	EB53	7B38	EE57	[火山]	[火山]	 	 	 
+FE03B	\uDBB8\uDC3B	\xF3\xBE\x80\xBB	MILKY WAY		781	F463	EB5F	7B44	EE63		172	F957	E6B3	7A47		422	FB8C	E44B	7A47	 	 	 
+FE03C	\uDBB8\uDC3C	\xF3\xBE\x80\xBC	FOUR LEAFED CLOVER		53	F6EC	E513	766E	EBEC		354	F9E6	E741	766E		106	F750	E110	766E	 		
+FE03D	\uDBB8\uDC3D	\xF3\xBE\x80\xBD	TULIP		113	F6BD	E4E4	763F	EBBD		356	F9E8	E743	763F		274	F9A4	E304	763F		 	
+FE03E	\uDBB8\uDC3E	\xF3\xBE\x80\xBE	SEEDLING		811	F482	EB7D	7B62	EE82		359	F9EB	E746	7B62		106	F750	E110	766E	 	 	
+FE03F	\uDBB8\uDC3F	\xF3\xBE\x80\xBF	MAPLE LEAF		133	F6A7	E4CE	7629	EBA7		360	F9EC	E747	7629		114	F758	E118	7629	 	 	
+FE040	\uDBB8\uDC40	\xF3\xBE\x81\x80	CHERRY BLOSSOM		235	F6A3	E4CA	7625	EBA3		361	F9ED	E748	7625		48	F970	E030	7625	 	 	
+FE041	\uDBB8\uDC41	\xF3\xBE\x81\x81	ROSE		339	F7EA	E5BA	786C	ECEA	[バラ]		50	F972	E032	786C	 	 	 
+FE042	\uDBB8\uDC42	\xF3\xBE\x81\x82	FALLEN LEAF		358	F340	E5CD	7921	ED40		360	F9EC	E747	7629		115	F759	E119	7921	 	 	 
+FE043	\uDBB8\uDC43	\xF3\xBE\x81\x83	LEAF FLUTTERING IN WIND		358	F340	E5CD	7921	ED40	〓		418	FB88	E447	7D4D	 	 	 
+FE044	\uDBB8\uDC44	\xF3\xBE\x81\x84	YOUNG GREEN LEAF		179	F658	E480	7539	EB58	[若葉マーク]		189	F7A9	E209	7539	 	 	
+FE045	\uDBB8\uDC45	\xF3\xBE\x81\x85	HIBISCUS		397	F367	EA94	7948	ED67	[ハイビスカス]		273	F9A3	E303	7948	 	 	 
+FE046	\uDBB8\uDC46	\xF3\xBE\x81\x86	SUNFLOWER		256	F6BC	E4E3	763E	EBBC	[ひまわり]		275	F9A5	E305	763E	 	 	 
+FE047	\uDBB8\uDC47	\xF3\xBE\x81\x87	PALM TREE		255	F6BB	E4E2	763D	EBBB	[ビーチ]		277	F9A7	E307	763D	 	 	 
+FE048	\uDBB8\uDC48	\xF3\xBE\x81\x88	CACTUS		399	F369	EA96	794A	ED69	[サボテン]		278	F9A8	E308	794A	 	 	 
+FE049	\uDBB8\uDC49	\xF3\xBE\x81\x89	EAR OF RICE	〓	〓		415	FB85	E444	7D65	 	 	 
+FE04A	\uDBB8\uDC4A	\xF3\xBE\x81\x8A	CORN		740	F3F7	EB36	7A79	EDF7	[とうもろこし]	[とうもろこし]	 	 	 
+FE04B	\uDBB8\uDC4B	\xF3\xBE\x81\x8B	MUSHROOM		741	F3F8	EB37	7A7A	EDF8	[キノコ]	[キノコ]	 	 	 
+FE04C	\uDBB8\uDC4C	\xF3\xBE\x81\x8C	CHESTNUT		742	F3F9	EB38	7A7B	EDF9	[栗]	[栗]	 	 	 
+FE04D	\uDBB8\uDC4D	\xF3\xBE\x81\x8D	FLOWER		759	F44D	EB49	7B2E	EE4D	[花]		275	F9A5	E305	763E		 	 
+FE04E	\uDBB8\uDC4E	\xF3\xBE\x81\x8E	HERB		816	F487	EB82	7B67	EE87		354	F9E6	E741	766E		106	F750	E110	766E	 	 	 
+FE04F	\uDBB8\uDC4F	\xF3\xBE\x81\x8F	CHERRIES		241	F6AB	E4D2	762D	EBAB		355	F9E7	E742	762D	[さくらんぼ]	 	 	
+FE050	\uDBB8\uDC50	\xF3\xBE\x81\x90	BANANA		739	F3F6	EB35	7A78	EDF6		357	F9E9	E744	7A78	[バナナ]	 	 	
+FE051	\uDBB8\uDC51	\xF3\xBE\x81\x91	APPLE		434	F38D	EAB9	796D	ED8D		358	F9EA	E745	796D		339	F9E5	E345	796D	 	 	
+FE052	\uDBB8\uDC52	\xF3\xBE\x81\x92	TANGERINE		435	F38E	EABA	796E	ED8E	[みかん]		340	F9E6	E346	796E	 	 	 
+FE053	\uDBB8\uDC53	\xF3\xBE\x81\x93	STRAWBERRY		243	F6AD	E4D4	762F	EBAD	[イチゴ]		341	F9E7	E347	762F	 	 	 
+FE054	\uDBB8\uDC54	\xF3\xBE\x81\x94	WATERMELON		238	F6A6	E4CD	7628	EBA6	[スイカ]		342	F9E8	E348	7628	 	 	 
+FE055	\uDBB8\uDC55	\xF3\xBE\x81\x95	TOMATO		436	F38F	EABB	796F	ED8F	[トマト]		343	F9E9	E349	796F	 	 	 
+FE056	\uDBB8\uDC56	\xF3\xBE\x81\x96	EGGPLANT		437	F390	EABC	7970	ED90	[ナス]		344	F9EA	E34A	7970	 	 	 
+FE057	\uDBB8\uDC57	\xF3\xBE\x81\x97	MELON		736	F3F3	EB32	7A75	EDF3	[メロン]	[メロン]	 	 	 
+FE058	\uDBB8\uDC58	\xF3\xBE\x81\x98	PINEAPPLE		737	F3F4	EB33	7A76	EDF4	[パイナップル]	[パイナップル]	 	 	 
+FE059	\uDBB8\uDC59	\xF3\xBE\x81\x99	GRAPES		738	F3F5	EB34	7A77	EDF5	[ブドウ]	[ブドウ]	 	 	 
+FE05A	\uDBB8\uDC5A	\xF3\xBE\x81\x9A	PEACH		743	F3FA	EB39	7A7C	EDFA	[モモ]	[モモ]	 	 	 
+FE05B	\uDBB8\uDC5B	\xF3\xBE\x81\x9B	GREEN APPLE		776	F45E	EB5A	7B3F	EE5E		358	F9EA	E745	796D		339	F9E5	E345	796D	 	 	 
+FE190	\uDBB8\uDD90	\xF3\xBE\x86\x90	EYES		317	F7C1	E5A4	7843	ECC1		84	F8F2	E691	7843		372	FB59	E419	7843	 	 	
+FE191	\uDBB8\uDD91	\xF3\xBE\x86\x91	EAR		318	F7C2	E5A5	7844	ECC2		85	F8F3	E692	7844		374	FB5B	E41B	7844	 	 	
+FE192	\uDBB8\uDD92	\xF3\xBE\x86\x92	NOSE		457	F3A4	EAD0	7A26	EDA4	[鼻]		373	FB5A	E41A	7A26	 	 	 
+FE193	\uDBB8\uDD93	\xF3\xBE\x86\x93	MOUTH		458	F3A5	EAD1	7A27	EDA5		149	F99E	E6F9	7646		375	FB5C	E41C	7A27	 	 	 
+FE194	\uDBB8\uDD94	\xF3\xBE\x86\x94	TONGUE		757	F44B	EB47	7B2C	EE4B		329	F9CD	E728	7642		356	FB49	E409	7D47	 	 	 
+FE195	\uDBB8\uDD95	\xF3\xBE\x86\x95	LIPSTICK		295	F6E2	E509	7664	EBE2		305	F9B5	E710	7664		298	F9BC	E31C	7664	 	 	
+FE196	\uDBB8\uDD96	\xF3\xBE\x86\x96	NAIL POLISH		409	F373	EAA0	7954	ED73	[マニキュア]		299	F9BD	E31D	7954	 	 	 
+FE197	\uDBB8\uDD97	\xF3\xBE\x86\x97	FACE MASSAGE		297	F6E4	E50B	7666	EBE4	[エステ]		300	F9BE	E31E	7666	 	 	 
+FE198	\uDBB8\uDD98	\xF3\xBE\x86\x98	HAIR NEEDING A CUT		410	F374	EAA1	7955	ED74		56	F8D6	E675	7671		301	F9BF	E31F	7955	 	 	 
+FE199	\uDBB8\uDD99	\xF3\xBE\x86\x99	BARBER POLE		411	F375	EAA2	7956	ED75	[床屋]		302	F9C0	E320	7956	 	 	 
+FE19A	\uDBB8\uDD9A	\xF3\xBE\x86\x9A	SILHOUETTE OF BUST	〓		170	F955	E6B1	7E2C	〓	 	 	
+FE19B	\uDBB8\uDD9B	\xF3\xBE\x86\x9B	BOYS HEAD		80	F6D5	E4FC	7657	EBD5		140	F995	E6F0	752A		1	F941	E001	7D37	 	 	 
+FE19C	\uDBB8\uDD9C	\xF3\xBE\x86\x9C	GIRLS HEAD		50	F6D3	E4FA	7655	EBD3		140	F995	E6F0	752A		2	F942	E002	7D32	 	 	 
+FE19D	\uDBB8\uDD9D	\xF3\xBE\x86\x9D	MANS HEAD		80	F6D5	E4FC	7657	EBD5		140	F995	E6F0	752A		4	F944	E004	7657	 	 	 
+FE19E	\uDBB8\uDD9E	\xF3\xBE\x86\x9E	WOMANS HEAD		50	F6D3	E4FA	7655	EBD3		140	F995	E6F0	752A		5	F945	E005	7655	 	 	 
+FE19F	\uDBB8\uDD9F	\xF3\xBE\x86\x9F	FAMILY		163	F6DA	E501	765C	EBDA	[家族]	[家族]	 	 	
+FE1A0	\uDBB8\uDDA0	\xF3\xBE\x86\xA0	CHILDREN	〓	〓		387	FB68	E428	7D64	 	 	 
+FE1A1	\uDBB8\uDDA1	\xF3\xBE\x86\xA1	POLICE		374	F350	E5DD	7931	ED50	[警官]		172	F793	E152	7931	 	 	 
+FE1A2	\uDBB8\uDDA2	\xF3\xBE\x86\xA2	WOMAN WITH BUNNY EARS		468	F3AF	EADB	7A31	EDAF	[バニー]		388	FB69	E429	7A31	 	 	 
+FE1A3	\uDBB8\uDDA3	\xF3\xBE\x86\xA3	BRIDE WITH VEIL		482	F3BD	EAE9	7A3F	EDBD	[花嫁]	[花嫁]	 	 	 
+FE1A4	\uDBB8\uDDA4	\xF3\xBE\x86\xA4	BLOND PERSON		705	F3D4	EB13	7A56	EDD4	[白人]		444	FBB5	E515	7A56	 	 	 
+FE1A5	\uDBB8\uDDA5	\xF3\xBE\x86\xA5	MAN WITH LONG MOUSTACHE		706	F3D5	EB14	7A57	EDD5	[中国人]		445	FBB6	E516	7A57			 
+FE1A6	\uDBB8\uDDA6	\xF3\xBE\x86\xA6	MAN WITH TURBAN		707	F3D6	EB15	7A58	EDD6	[インド]		446	FBB7	E517	7A58	 	 	 
+FE1A7	\uDBB8\uDDA7	\xF3\xBE\x86\xA7	OLDER MAN		708	F3D7	EB16	7A59	EDD7	[おじいさん]		447	FBB8	E518	7A59	 	 	 
+FE1A8	\uDBB8\uDDA8	\xF3\xBE\x86\xA8	OLDER WOMAN		709	F3D8	EB17	7A5A	EDD8	[おばあさん]		448	FBB9	E519	7A5A	 	 	 
+FE1A9	\uDBB8\uDDA9	\xF3\xBE\x86\xA9	BABY		710	F3D9	EB18	7A5B	EDD9	[赤ちゃん]		449	FBBA	E51A	7A5B	 	 	 
+FE1AA	\uDBB8\uDDAA	\xF3\xBE\x86\xAA	CONSTRUCTION WORKER		711	F3DA	EB19	7A5C	EDDA	[工事現場の人]		450	FBBB	E51B	7A5C	 	 	 
+FE1AB	\uDBB8\uDDAB	\xF3\xBE\x86\xAB	PRINCESS		712	F3DB	EB1A	7A5D	EDDB	[お姫様]		451	FBBC	E51C	7A5D	 	 	 
+FE1AC	\uDBB8\uDDAC	\xF3\xBE\x86\xAC	RED FACED OGRE		754	F448	EB44	7B29	EE48	[なまはげ]	[なまはげ]	 	 	 
+FE1AD	\uDBB8\uDDAD	\xF3\xBE\x86\xAD	LONG NOSED GOBLIN		755	F449	EB45	7B2A	EE49	[天狗]	[天狗]	 	 	 
+FE1AE	\uDBB8\uDDAE	\xF3\xBE\x86\xAE	GHOST		236	F6A4	E4CB	7626	EBA4	[お化け]		117	F75B	E11B	7626	 		 
+FE1AF	\uDBB8\uDDAF	\xF3\xBE\x86\xAF	ANGEL		344	F7EF	E5BF	7871	ECEF	[天使]		78	F98F	E04E	7871	 		 
+FE1B0	\uDBB8\uDDB0	\xF3\xBE\x86\xB0	EXTRATERRESTRIAL ALIEN		302	F6E7	E50E	7669	EBE7	[UFO]		102	F74C	E10C	7D48	 	 	 
+FE1B1	\uDBB8\uDDB1	\xF3\xBE\x86\xB1	ALIEN MONSTER		274	F6C5	E4EC	7647	EBC5	[宇宙人]		133	F76B	E12B	7647	 	 	 
+FE1B2	\uDBB8\uDDB2	\xF3\xBE\x86\xB2	DEVIL		277	F6C8	E4EF	764A	EBC8	[アクマ]		116	F75A	E11A	764A			 
+FE1B3	\uDBB8\uDDB3	\xF3\xBE\x86\xB3	SKULL		286	F6D1	E4F8	7653	EBD1	[ドクロ]		118	F75C	E11C	7653	 	 	 
+FE1B4	\uDBB8\uDDB4	\xF3\xBE\x86\xB4	NEWSCASTER	〓	〓		263	F7F3	E253	7D62	 	 	 
+FE1B5	\uDBB8\uDDB5	\xF3\xBE\x86\xB5	YEOMAN OF THE GUARD	〓	〓		453	FBBE	E51E	7D68	 	 	 
+FE1B6	\uDBB8\uDDB6	\xF3\xBE\x86\xB6	DANCER		714	F3DD	EB1C	7A5F	EDDD	[ダンス]		454	FBBF	E51F	7A5F	 	 	 
+FE1B7	\uDBB8\uDDB7	\xF3\xBE\x86\xB7	DOG		134	F6BA	E4E1	763C	EBBA		100	F945	E6A1	763C		82	F993	E052	763C	 	 	
+FE1B8	\uDBB8\uDDB8	\xF3\xBE\x86\xB8	CAT		251	F6B4	E4DB	7636	EBB4		101	F946	E6A2	7636		79	F990	E04F	7636	 	 	
+FE1B9	\uDBB8\uDDB9	\xF3\xBE\x86\xB9	SNAIL		812	F483	EB7E	7B63	EE83		367	F9F3	E74E	7B63	[カタツムリ]	 	 	
+FE1BA	\uDBB8\uDDBA	\xF3\xBE\x86\xBA	BABY CHICK		78	F6B9	E4E0	763B	EBB9		368	F9F4	E74F	763B		458	FBC3	E523	763B	 	 	
+FE1BB	\uDBB8\uDDBB	\xF3\xBE\x86\xBB	BABY CHICK 2		804	F47A	EB76	7B5B	EE7A		368	F9F4	E74F	763B		458	FBC3	E523	763B	 	 	 
+FE1BC	\uDBB8\uDDBC	\xF3\xBE\x86\xBC	PENGUIN		252	F6B5	E4DC	7637	EBB5		369	F9F5	E750	7637		85	F996	E055	7637	 	 	
+FE1BD	\uDBB8\uDDBD	\xF3\xBE\x86\xBD	FISH		203	F672	E49A	7553	EB72		370	F9F6	E751	7A60		25	F959	E019	7D41	 	 	
+FE1BE	\uDBB8\uDDBE	\xF3\xBE\x86\xBE	HORSE		248	F6B1	E4D8	7633	EBB1		373	F9F9	E754	7633		26	F95A	E01A	7633	 	 	
+FE1BF	\uDBB8\uDDBF	\xF3\xBE\x86\xBF	PIG		254	F6B7	E4DE	7639	EBB7		374	F9FA	E755	7639		101	F74B	E10B	7639	 		
+FE1C0	\uDBB8\uDDC0	\xF3\xBE\x87\x80	TIGER		345	F7F0	E5C0	7872	ECF0	[トラ]		80	F991	E050	7872	 	 	 
+FE1C1	\uDBB8\uDDC1	\xF3\xBE\x87\x81	BEAR		346	F7F1	E5C1	7873	ECF1	[クマ]		81	F992	E051	7873	 	 	 
+FE1C2	\uDBB8\uDDC2	\xF3\xBE\x87\x82	MOUSE		347	F7F2	E5C2	7874	ECF2	[ネズミ]		83	F994	E053	7874	 	 	 
+FE1C3	\uDBB8\uDDC3	\xF3\xBE\x87\x83	WHALE		246	F648	E470	7529	EB48	[クジラ]		84	F995	E054	7529	 	 	 
+FE1C4	\uDBB8\uDDC4	\xF3\xBE\x87\x84	MONKEY FACE		249	F6B2	E4D9	7634	EBB2	〓		99	F749	E109	7634			 
+FE1C5	\uDBB8\uDDC5	\xF3\xBE\x87\x85	OCTOPUS		352	F7F7	E5C7	7879	ECF7	[タコ]		100	F74A	E10A	7879	 	 	 
+FE1C6	\uDBB8\uDDC6	\xF3\xBE\x87\x86	SPIRAL SHELL		485	F3C0	EAEC	7A42	EDC0	[巻貝]		412	FB82	E441	7A42	 	 	 
+FE1C7	\uDBB8\uDDC7	\xF3\xBE\x87\x87	DOLPHIN		713	F3DC	EB1B	7A5E	EDDC	[イルカ]		455	FBC0	E520	7A5E	 	 	 
+FE1C8	\uDBB8\uDDC8	\xF3\xBE\x87\x88	BIRD		78	F6B9	E4E0	763B	EBB9		368	F9F4	E74F	763B		456	FBC1	E521	7D36	 	 	 
+FE1C9	\uDBB8\uDDC9	\xF3\xBE\x87\x89	TROPICAL FISH		715	F3DE	EB1D	7A60	EDDE		370	F9F6	E751	7A60		457	FBC2	E522	7A60	 	 	 
+FE1CA	\uDBB8\uDDCA	\xF3\xBE\x87\x8A	HAMSTER	〓	〓		459	FBC4	E524	7D69	 	 	 
+FE1CB	\uDBB8\uDDCB	\xF3\xBE\x87\x8B	CENTIPEDE		716	F3DF	EB1E	7A61	EDDF	[ゲジゲジ]		460	FBC5	E525	7A61	 	 	 
+FE1CC	\uDBB8\uDDCC	\xF3\xBE\x87\x8C	ELEPHANT		717	F3E0	EB1F	7A62	EDE0	[ゾウ]		461	FBC6	E526	7A62	 	 	 
+FE1CD	\uDBB8\uDDCD	\xF3\xBE\x87\x8D	KOALA		718	F3E1	EB20	7A63	EDE1	[コアラ]		462	FBC7	E527	7A63	 	 	 
+FE1CE	\uDBB8\uDDCE	\xF3\xBE\x87\x8E	MONKEY		249	F6B2	E4D9	7634	EBB2	[サル]		463	FBC8	E528	7D45	 	 	 
+FE1CF	\uDBB8\uDDCF	\xF3\xBE\x87\x8F	SHEEP		192	F667	E48F	7548	EB67	[ヒツジ]		464	FBC9	E529	7D40	 	 	 
+FE1D0	\uDBB8\uDDD0	\xF3\xBE\x87\x90	FOX		134	F6BA	E4E1	763C	EBBA		100	F945	E6A1	763C		465	FBCA	E52A	7D3D	 	 	 
+FE1D1	\uDBB8\uDDD1	\xF3\xBE\x87\x91	COW		719	F3E2	EB21	7A64	EDE2	[牛]		466	FBCB	E52B	7A64	 	 	 
+FE1D2	\uDBB8\uDDD2	\xF3\xBE\x87\x92	RABBIT		247	F6B0	E4D7	7632	EBB0	[ウサギ]		467	FBCC	E52C	7632	 	 	 
+FE1D3	\uDBB8\uDDD3	\xF3\xBE\x87\x93	SNAKE		720	F3E3	EB22	7A65	EDE3	[ヘビ]		468	FBCD	E52D	7A65	 	 	 
+FE1D4	\uDBB8\uDDD4	\xF3\xBE\x87\x94	CHICKEN		721	F3E4	EB23	7A66	EDE4	[ニワトリ]		469	FBCE	E52E	7A66	 	 	 
+FE1D5	\uDBB8\uDDD5	\xF3\xBE\x87\x95	BOAR		722	F3E5	EB24	7A67	EDE5	[イノシシ]		470	FBCF	E52F	7A67	 		 
+FE1D6	\uDBB8\uDDD6	\xF3\xBE\x87\x96	CAMEL		723	F3E6	EB25	7A68	EDE6	[ラクダ]		471	FBD0	E530	7A68	 	 	 
+FE1D7	\uDBB8\uDDD7	\xF3\xBE\x87\x97	FROG		250	F6B3	E4DA	7635	EBB3	[カエル]		472	FBD1	E531	7635	 	 	 
+FE1D8	\uDBB8\uDDD8	\xF3\xBE\x87\x98	POODLE		74	F6B8	E4DF	763A	EBB8		100	F945	E6A1	763C		82	F993	E052	763C	 	 	 
+FE1D9	\uDBB8\uDDD9	\xF3\xBE\x87\x99	BLOWFISH		242	F6AC	E4D3	762E	EBAC		370	F9F6	E751	7A60		25	F959	E019	7D41	 	 	 
+FE1DA	\uDBB8\uDDDA	\xF3\xBE\x87\x9A	ANT		253	F6B6	E4DD	7638	EBB6	[アリ]	[アリ]	 	 	 
+FE1DB	\uDBB8\uDDDB	\xF3\xBE\x87\x9B	DOG PRINTS		276	F6C7	E4EE	7649	EBC7		91	F8F9	E698	7A6D		477	FBD6	E536	7A6D	 	 	 
+FE1DC	\uDBB8\uDDDC	\xF3\xBE\x87\x9C	TURTLE		365	F347	E5D4	7928	ED47	[カメ]	[カメ]	 	 	 
+FE1DD	\uDBB8\uDDDD	\xF3\xBE\x87\x9D	HATCHING CHICK		372	F34E	E5DB	792F	ED4E		368	F9F4	E74F	763B		458	FBC3	E523	763B	 	 	 
+FE1DE	\uDBB8\uDDDE	\xF3\xBE\x87\x9E	CHINESE ZODIAC DRAGON		749	F443	EB3F	7B24	EE43	[辰]	[辰]	 	 	 
+FE1DF	\uDBB8\uDDDF	\xF3\xBE\x87\x9F	PANDA		756	F44A	EB46	7B2B	EE4A	[パンダ]	[パンダ]	 	 	 
+FE1E0	\uDBB8\uDDE0	\xF3\xBE\x87\xA0	PIGS NOSE		758	F44C	EB48	7B2D	EE4C		374	F9FA	E755	7639		101	F74B	E10B	7639		 	 
+FE1E1	\uDBB8\uDDE1	\xF3\xBE\x87\xA1	HONEY BEE		773	F45B	EB57	7B3C	EE5B	[ミツバチ]	[ミツバチ]	 	 	 
+FE1E2	\uDBB8\uDDE2	\xF3\xBE\x87\xA2	LADYBUG		774	F45C	EB58	7B3D	EE5C	[てんとう虫]	[てんとう虫]	 	 	 
+FE1E3	\uDBB8\uDDE3	\xF3\xBE\x87\xA3	CRAB		195	F66A	E492	754B	EB6A	[カニ]	[カニ]			 
+FE320	\uDBB8\uDF20	\xF3\xBE\x8C\xA0	ANGRY FACE		258	F64A	E472	752B	EB4A		141	F996	E6F1	752B		89	F99A	E059	752B			
+FE321	\uDBB8\uDF21	\xF3\xBE\x8C\xA1	ANGUISHED FACE		789	F46B	EB67	7B4C	EE6B		143	F998	E6F3	7977		350	FB43	E403	7974	 		 
+FE322	\uDBB8\uDF22	\xF3\xBE\x8C\xA2	ASTONISHED FACE		451	F39E	EACA	797E	ED9E		144	F999	E6F4	784D		363	FB50	E410	797E			 
+FE323	\uDBB8\uDF23	\xF3\xBE\x8C\xA3	DISAPPOINTED FACE		441	F394	EAC0	7974	ED94		142	F997	E6F2	7974		88	F999	E058	7977			
+FE324	\uDBB8\uDF24	\xF3\xBE\x8C\xA4	DIZZY FACE		327	F7CB	E5AE	784D	ECCB		144	F999	E6F4	784D		353	FB46	E406	7976			
+FE325	\uDBB8\uDF25	\xF3\xBE\x8C\xA5	EXASPERATED FACE		452	F39F	EACB	7A21	ED9F		324	F9C8	E723	7878		362	FB4F	E40F	7D50	 		 
+FE326	\uDBB8\uDF26	\xF3\xBE\x8C\xA6	EXPRESSIONLESS FACE		450	F39D	EAC9	797D	ED9D		326	F9CA	E725	797D		361	FB4E	E40E	797D			
+FE327	\uDBB8\uDF27	\xF3\xBE\x8C\xA7	FACE WITH HEART SHAPED EYES		349	F7F4	E5C4	7876	ECF4		327	F9CB	E726	7876		96	F746	E106	7876		 	
+FE328	\uDBB8\uDF28	\xF3\xBE\x8C\xA8	FACE WITH LOOK OF TRIUMPH		442	F395	EAC1	7975	ED95		372	F9F8	E753	7B65		351	FB44	E404	7975	 		 
+FE329	\uDBB8\uDF29	\xF3\xBE\x8C\xA9	FACE WITH STUCK OUT TONGUE		264	F6C0	E4E7	7642	EBC0		329	F9CD	E728	7642		95	F745	E105	7642			
+FE32A	\uDBB8\uDF2A	\xF3\xBE\x8C\xAA	FACE WITH STUCK OUT TONGUE 2		264	F6C0	E4E7	7642	EBC0		329	F9CD	E728	7642		356	FB49	E409	7D47	 		 
+FE32B	\uDBB8\uDF2B	\xF3\xBE\x8C\xAB	FACE WITH YUMMY LOOK		454	F3A1	EACD	7A23	EDA1		371	F9F7	E752	7A23		86	F997	E056	7A23			
+FE32C	\uDBB8\uDF2C	\xF3\xBE\x8C\xAC	FACE WITH PUCKERED LIPS		456	F3A3	EACF	7A25	EDA3		327	F9CB	E726	7876		371	FB58	E418	7A25	 		 
+FE32D	\uDBB8\uDF2D	\xF3\xBE\x8C\xAD	FACE WITH PUCKERED LIPS 2		455	F3A2	EACE	7A24	EDA2		327	F9CB	E726	7876		370	FB57	E417	7A24	 	 	 
+FE32E	\uDBB8\uDF2E	\xF3\xBE\x8C\xAE	FACE WITH A MASK		448	F39B	EAC7	797B	ED9B	[風邪ひき]		359	FB4C	E40C	797B	 		 
+FE32F	\uDBB8\uDF2F	\xF3\xBE\x8C\xAF	FEVERISH FACE		449	F39C	EAC8	797C	ED9C		331	F9CF	E72A	7E35		360	FB4D	E40D	797C			 
+FE330	\uDBB8\uDF30	\xF3\xBE\x8C\xB0	HAPPY FACE		257	F649	E471	752A	EB49		140	F995	E6F0	752A		87	F998	E057	752A			
+FE331	\uDBB8\uDF31	\xF3\xBE\x8C\xB1	HAPPY FACE 2		257+330	F649+F7CE	E471+E5B1	752A+7850	EB49+ECCE		323	F9C7	E722	7E34		368+319	FB55+F9D1	E415+E331	7D46+7850			
+FE332	\uDBB8\uDF32	\xF3\xBE\x8C\xB2	HAPPY FACE 3		446	F399	EAC5	7979	ED99		331	F9CF	E72A	7E35		357	FB4A	E40A	7979			
+FE333	\uDBB8\uDF33	\xF3\xBE\x8C\xB3	HAPPY FACE 4		814	F485	EB80	7B65	EE85		372	F9F8	E753	7B65		351	FB44	E404	7975			
+FE334	\uDBB8\uDF34	\xF3\xBE\x8C\xB4	HAPPY FACE 5		786	F468	EB64	7B49	EE68		331	F9CF	E72A	7E35		365	FB52	E412	7B49	 	 	 
+FE335	\uDBB8\uDF35	\xF3\xBE\x8C\xB5	HAPPY FACE 6		454	F3A1	EACD	7A23	EDA1		140	F995	E6F0	752A		86	F997	E056	7A23	 		 
+FE336	\uDBB8\uDF36	\xF3\xBE\x8C\xB6	HAPPY FACE 7		68	F6D4	E4FB	7656	EBD4		140	F995	E6F0	752A		86	F997	E056	7A23	 	 	 
+FE337	\uDBB8\uDF37	\xF3\xBE\x8C\xB7	HAPPY FACE 8		454	F3A1	EACD	7A23	EDA1		140	F995	E6F0	752A		367	FB54	E414	7D51	 	 	 
+FE338	\uDBB8\uDF38	\xF3\xBE\x8C\xB8	HAPPY FACE 9		257	F649	E471	752A	EB49		140	F995	E6F0	752A		368	FB55	E415	7D46			 
+FE339	\uDBB8\uDF39	\xF3\xBE\x8C\xB9	LITTLE CRYING FACE		791	F46D	EB69	7B4E	EE6D		335	F9D3	E72E	7B4E		366	FB53	E413	7B4E	 	 	
+FE33A	\uDBB8\uDF3A	\xF3\xBE\x8C\xBA	LOUDLY CRYING FACE		259	F64B	E473	752C	EB4B		334	F9D2	E72D	752C		364	FB51	E411	752C		 	
+FE33B	\uDBB8\uDF3B	\xF3\xBE\x8C\xBB	PALE FACE		447	F39A	EAC6	797A	ED9A		376	F9FC	E757	7877		358	FB4B	E40B	797A	 		 
+FE33C	\uDBB8\uDF3C	\xF3\xBE\x8C\xBC	PERSEVERING FACE		443	F396	EAC2	7976	ED96		332	F9D0	E72B	7976		353	FB46	E406	7976			
+FE33D	\uDBB8\uDF3D	\xF3\xBE\x8C\xBD	POUTING FACE		779	F461	EB5D	7B42	EE61		325	F9C9	E724	7B42		369	FB56	E416	7B42			
+FE33E	\uDBB8\uDF3E	\xF3\xBE\x8C\xBE	RELIEVED FACE		446	F399	EAC5	7979	ED99		322	F9C6	E721	7979		357	FB4A	E40A	7979			
+FE33F	\uDBB8\uDF3F	\xF3\xBE\x8C\xBF	SAD FACE		444	F397	EAC3	7977	ED97		143	F998	E6F3	7977		354	FB47	E407	7D4F			
+FE340	\uDBB8\uDF40	\xF3\xBE\x8D\x80	SAD FACE 2		441	F394	EAC0	7974	ED94		321	F9C5	E720	7E33		350	FB43	E403	7974	 		
+FE341	\uDBB8\uDF41	\xF3\xBE\x8D\x81	SHOCKED FACE WITH SCREAM		350	F7F5	E5C5	7877	ECF5		376	F9FC	E757	7877		97	F747	E107	7877			
+FE342	\uDBB8\uDF42	\xF3\xBE\x8D\x82	SLEEPY FACE		445	F398	EAC4	7978	ED98		157	F9A6	E701	752E		355	FB48	E408	7978		 	 
+FE343	\uDBB8\uDF43	\xF3\xBE\x8D\x83	SMIRKY FACE		440	F393	EABF	7973	ED93		333	F9D1	E72C	7973		349	FB42	E402	7973		 	
+FE344	\uDBB8\uDF44	\xF3\xBE\x8D\x84	TEARY FACE		351	F7F6	E5C6	7878	ECF6		324	F9C8	E723	7878		98	F748	E108	7878			
+FE345	\uDBB8\uDF45	\xF3\xBE\x8D\x85	TEARY FACE 2		351	F7F6	E5C6	7878	ECF6		324	F9C8	E723	7878		348	FB41	E401	7D4B	 	 	 
+FE346	\uDBB8\uDF46	\xF3\xBE\x8D\x86	TIRED FACE		260	F64C	E474	752D	EB4C		332	F9D0	E72B	7976		353	FB46	E406	7976	 		 
+FE347	\uDBB8\uDF47	\xF3\xBE\x8D\x87	WINKING FACE		348	F7F3	E5C3	7875	ECF3		330	F9CE	E729	7875		352	FB45	E405	7875			
+FE348	\uDBB8\uDF48	\xF3\xBE\x8D\x88	CAT FACE 1		783	F465	EB61	7B46	EE65		140	F995	E6F0	752A		87	F998	E057	752A	 	 	 
+FE349	\uDBB8\uDF49	\xF3\xBE\x8D\x89	CAT FACE 2		813	F484	EB7F	7B64	EE84		372	F9F8	E753	7B65		351	FB44	E404	7975		 	 
+FE34A	\uDBB8\uDF4A	\xF3\xBE\x8D\x8A	CAT FACE 3		785	F467	EB63	7B48	EE67		331	F9CF	E72A	7E35		365	FB52	E412	7B49	 	 	 
+FE34B	\uDBB8\uDF4B	\xF3\xBE\x8D\x8B	CAT FACE 4		782	F464	EB60	7B45	EE64		327	F9CB	E726	7876		371	FB58	E418	7A25	 	 	 
+FE34C	\uDBB8\uDF4C	\xF3\xBE\x8D\x8C	CAT FACE 5		787	F469	EB65	7B4A	EE69		327	F9CB	E726	7876		96	F746	E106	7876	 	 	 
+FE34D	\uDBB8\uDF4D	\xF3\xBE\x8D\x8D	CAT FACE 6		790	F46C	EB68	7B4D	EE6C		335	F9D3	E72E	7B4E		366	FB53	E413	7B4E	 	 	 
+FE34E	\uDBB8\uDF4E	\xF3\xBE\x8D\x8E	CAT FACE 7		780	F462	EB5E	7B43	EE62		325	F9C9	E724	7B42		369	FB56	E416	7B42	 	 	 
+FE34F	\uDBB8\uDF4F	\xF3\xBE\x8D\x8F	CAT FACE 8		792	F46E	EB6A	7B4F	EE6E		372	F9F8	E753	7B65		351	FB44	E404	7975	 	 	 
+FE350	\uDBB8\uDF50	\xF3\xBE\x8D\x90	CAT FACE 9		788	F46A	EB66	7B4B	EE6A		143	F998	E6F3	7977		350	FB43	E403	7974	 	 	 
+FE351	\uDBB8\uDF51	\xF3\xBE\x8D\x91	FACE WITH NO GOOD GESTURE		464	F3AB	EAD7	7A2D	EDAB		336	F9D4	E72F	7E36		382	FB63	E423	7A2D	 	 	 
+FE352	\uDBB8\uDF52	\xF3\xBE\x8D\x92	FACE WITH OK GESTURE		465	F3AC	EAD8	7A2E	EDAC		135	F9B0	E70B	784C		383	FB64	E424	7A2E	 	 	 
+FE353	\uDBB8\uDF53	\xF3\xBE\x8D\x93	BOWING FACE		466	F3AD	EAD9	7A2F	EDAD	m(_ _)m		385	FB66	E426	7A2F	 	 	 
+FE354	\uDBB8\uDF54	\xF3\xBE\x8D\x94	SEE NO EVIL MONKEY		766	F454	EB50	7B35	EE54	(/_\)	(/_\)	 	 	 
+FE355	\uDBB8\uDF55	\xF3\xBE\x8D\x95	SPEAK NO EVIL MONEY		767	F455	EB51	7B36	EE55	(・×・)	(・×・)	 	 	 
+FE356	\uDBB8\uDF56	\xF3\xBE\x8D\x96	HEAR NO EVIL MONKEY		768	F456	EB52	7B37	EE56	|(・×・)|	|(・×・)|	 	 	 
+FE357	\uDBB8\uDF57	\xF3\xBE\x8D\x97	PERSON RAISING ONE HAND		819	F48A	EB85	7B6A	EE8A	(^-^)/		18	F952	E012	7846	 	 	 
+FE358	\uDBB8\uDF58	\xF3\xBE\x8D\x98	PERSON RAISING BOTH HANDS		820	F48B	EB86	7B6B	EE8B	\(^o^)/		386	FB67	E427	7B6B	 	 	 
+FE359	\uDBB8\uDF59	\xF3\xBE\x8D\x99	PERSON FROWNING		821	F48C	EB87	7B6C	EE8C		143	F998	E6F3	7977		350	FB43	E403	7974	 	 	 
+FE35A	\uDBB8\uDF5A	\xF3\xBE\x8D\x9A	PERSON MAKING POUTING FACE		822	F48D	EB88	7B6D	EE8D		141	F996	E6F1	752B		369	FB56	E416	7B42	 		 
+FE35B	\uDBB8\uDF5B	\xF3\xBE\x8D\x9B	PERSON WITH FOLDED HANDS		459	F3A6	EAD2	7A28	EDA6	(>人<)		376	FB5D	E41D	7A28	 	 	 
+FE35C	\uDBB8\uDF5C	\xF3\xBE\x8D\x9C	COOL FACE	B-)	B-)	B-)			 
+FE35D	\uDBB8\uDF5D	\xF3\xBE\x8D\x9D	HUG FACE	\(^-^)/	\(^-^)/	\(^-^)/			 
+FE35E	\uDBB8\uDF5E	\xF3\xBE\x8D\x9E	GEEK	8-|	8-|	8-|			 
+FE35F	\uDBB8\uDF5F	\xF3\xBE\x8D\x9F	THINKING	[考え中]	[考え中]	[考え中]		 	 
+FE360	\uDBB8\uDF60	\xF3\xBE\x8D\xA0	BOUNCING HAPPY	[やったー]	[やったー]	[やったー]		 	 
+FE361	\uDBB8\uDF61	\xF3\xBE\x8D\xA1	FACE WITH ROLLING EYES	[クラクラ]	[クラクラ]	[クラクラ]			 
+FE362	\uDBB8\uDF62	\xF3\xBE\x8D\xA2	FACE WITH SLANTED MOUTH	[ムムム]	[ムムム]	[ムムム]			 
+FE363	\uDBB8\uDF63	\xF3\xBE\x8D\xA3	FACE WITH UNBALANCED EYES	[エッ?]	[エッ?]	[エッ?]			 
+FE364	\uDBB8\uDF64	\xF3\xBE\x8D\xA4	UPSIDE DOWN FACE	[逆立ち]	[逆立ち]	[逆立ち]			 
+FE365	\uDBB8\uDF65	\xF3\xBE\x8D\xA5	INJURED FACE	[怪我をした顔]	[怪我をした顔]	[怪我をした顔]	 		 
+FE366	\uDBB8\uDF66	\xF3\xBE\x8D\xA6	NERVOUS FACE	[心配した顔]	[心配した顔]	[心配した顔]	 		 
+FE367	\uDBB8\uDF67	\xF3\xBE\x8D\xA7	SYMPATHETIC FACE	[同情した顔]	[同情した顔]	[同情した顔]	 		 
+FE368	\uDBB8\uDF68	\xF3\xBE\x8D\xA8	THIN FACE	[細い顔]	[細い顔]	[細い顔]	 		 
+FE369	\uDBB8\uDF69	\xF3\xBE\x8D\xA9	ROBOT	[ロボット]	[ロボット]	[ロボット]		 	 
+FE4B0	\uDBB9\uDCB0	\xF3\xBE\x92\xB0	HOUSE		112	F684	E4AB	7564	EB84		38	F8C4	E663	7564		54	F976	E036	7564		 	
+FE4B1	\uDBB9\uDCB1	\xF3\xBE\x92\xB1	HOME		514	F7E0	EB09	7862	ECE0		38	F8C4	E663	7564		54	F976	E036	7564	 	 	 
+FE4B2	\uDBB9\uDCB2	\xF3\xBE\x92\xB2	OFFICE BUILDING		156	F686	E4AD	7566	EB86		39	F8C5	E664	7566		56	F978	E038	7566	 	 	
+FE4B3	\uDBB9\uDCB3	\xF3\xBE\x92\xB3	POST OFFICE		375	F351	E5DE	7932	ED51		40	F8C6	E665	7932		173	F794	E153	7932	 	 	
+FE4B4	\uDBB9\uDCB4	\xF3\xBE\x92\xB4	HOSPITAL		376	F352	E5DF	7933	ED52		41	F8C7	E666	7933		175	F796	E155	7933	 	 	
+FE4B5	\uDBB9\uDCB5	\xF3\xBE\x92\xB5	BANK		212	F683	E4AA	7563	EB83		42	F8C8	E667	7563		167	F78E	E14D	7563	 	 	
+FE4B6	\uDBB9\uDCB6	\xF3\xBE\x92\xB6	ATM		205	F67B	E4A3	755C	EB7B		43	F8C9	E668	755C		174	F795	E154	755C	 	 	
+FE4B7	\uDBB9\uDCB7	\xF3\xBE\x92\xB7	HOTEL		378	F354	EA81	7935	ED54		44	F8CA	E669	7935		178	F799	E158	7935	 	 	
+FE4B8	\uDBB9\uDCB8	\xF3\xBE\x92\xB8	HOTEL 2		492	F3C7	EAF3	7A49	EDC7		44+139	F8CA+F994	E669+E6EF	7935+7531		424	FBA1	E501	7A49	 	 	 
+FE4B9	\uDBB9\uDCB9	\xF3\xBE\x92\xB9	CONVENIENCE STORE		206	F67C	E4A4	755D	EB7C		45	F8CB	E66A	755D		176	F797	E156	755D	 	 	
+FE4BA	\uDBB9\uDCBA	\xF3\xBE\x92\xBA	SCHOOL		377	F353	EA80	7934	ED53		351	F9E3	E73E	7934		177	F798	E157	7934	 	 	
+FE4BB	\uDBB9\uDCBB	\xF3\xBE\x92\xBB	CHAPEL		340	F7EB	E5BB	786D	ECEB	[教会]		55	F977	E037	786D	 	 	 
+FE4BC	\uDBB9\uDCBC	\xF3\xBE\x92\xBC	FOUNTAIN		360	F342	E5CF	7923	ED42	[噴水]		123	F761	E121	7923	 	 	 
+FE4BD	\uDBB9\uDCBD	\xF3\xBE\x92\xBD	DEPARTMENT STORE		495	F3CA	EAF6	7A4C	EDCA	[デパート]		427	FBA4	E504	7A4C	 	 	 
+FE4BE	\uDBB9\uDCBE	\xF3\xBE\x92\xBE	JAPANESE CASTLE		496	F3CB	EAF7	7A4D	EDCB	[城]		428	FBA5	E505	7A4D	 	 	 
+FE4BF	\uDBB9\uDCBF	\xF3\xBE\x92\xBF	WESTERN CASTLE		497	F3CC	EAF8	7A4E	EDCC	[城]		429	FBA6	E506	7A4E	 	 	 
+FE4C0	\uDBB9\uDCC0	\xF3\xBE\x93\x80	FACTORY		498	F3CD	EAF9	7A4F	EDCD	[工場]		431	FBA8	E508	7A4F	 	 	 
+FE4C1	\uDBB9\uDCC1	\xF3\xBE\x93\x81	ANCHOR		211	F682	E4A9	7562	EB82		36	F8C2	E661	7936		182	F7A2	E202	7936	 	 	 
+FE4C2	\uDBB9\uDCC2	\xF3\xBE\x93\x82	RED LANTERN		225	F696	E4BD	7576	EB96		364	F9F0	E74B	794B		281	F9AB	E30B	794B	 	 	 
+FE4C3	\uDBB9\uDCC3	\xF3\xBE\x93\x83	MOUNT FUJI		342	F7ED	E5BD	786F	ECED		353	F9E5	E740	786F		59	F97B	E03B	786F	 	 	
+FE4C4	\uDBB9\uDCC4	\xF3\xBE\x93\x84	TOKYO TOWER		228	F699	E4C0	7579	EB99	[東京タワー]		432	FBA9	E509	7579	 	 	 
+FE4C5	\uDBB9\uDCC5	\xF3\xBE\x93\x85	BOUTIQUE 109	〓	〓		433	FBAA	E50A	7D66	 	 	 
+FE4C6	\uDBB9\uDCC6	\xF3\xBE\x93\x86	STATUE OF LIBERTY	〓	〓		452	FBBD	E51D	7D67	 	 	 
+FE4C7	\uDBB9\uDCC7	\xF3\xBE\x93\x87	MAP OF JAPAN		214	F78F	E572	776F	EC8F	[地図]	[地図]	 	 	 
+FE4C8	\uDBB9\uDCC8	\xF3\xBE\x93\x88	MOYAI		794	F470	EB6C	7B51	EE70	[モアイ]	[モアイ]	 	 	 
+FE4C9	\uDBB9\uDCC9	\xF3\xBE\x93\x89	WRENCH		152	F7A4	E587	7826	ECA4		313	F9BD	E718	7826	[レンチ]	 	 	
+FE4CA	\uDBB9\uDCCA	\xF3\xBE\x93\x8A	HAMMER		356	F7FB	E5CB	787D	ECFB	[ハンマー]		112	F756	E116	787D	 	 	 
+FE4CB	\uDBB9\uDCCB	\xF3\xBE\x93\x8B	NUT AND BOLT		123	F79E	E581	777E	EC9E	[ネジ]	[ネジ]	 	 	 
+FE4CC	\uDBB9\uDCCC	\xF3\xBE\x93\x8C	MENS SHOE		336	F7E7	E5B7	7869	ECE7		92	F8FA	E699	7A6E		7	F947	E007	7869	 	 	 
+FE4CD	\uDBB9\uDCCD	\xF3\xBE\x93\x8D	SNEAKER		729	F3EC	EB2B	7A6E	EDEC		92	F8FA	E699	7A6E		7	F947	E007	7869	 	 	
+FE4CE	\uDBB9\uDCCE	\xF3\xBE\x93\x8E	GLASSES		116	F6D7	E4FE	7659	EBD7		93	F8FB	E69A	7659	[メガネ]	 	 	
+FE4CF	\uDBB9\uDCCF	\xF3\xBE\x93\x8F	T-SHIRT		335	F7E6	E5B6	7868	ECE6		303	F9B3	E70E	7868		6	F946	E006	7868	 	 	
+FE4D0	\uDBB9\uDCD0	\xF3\xBE\x93\x90	JEANS		805	F47B	EB77	7B5C	EE7B		306	F9B6	E711	7B5C	[ジーンズ]	 	 	
+FE4D1	\uDBB9\uDCD1	\xF3\xBE\x93\x91	CROWN		354	F7F9	E5C9	787B	ECF9		315	F9BF	E71A	787B		104	F74E	E10E	787B	 	 	
+FE4D2	\uDBB9\uDCD2	\xF3\xBE\x93\x92	EMBLEM		354	F7F9	E5C9	787B	ECF9		315	F9BF	E71A	787B		49	F971	E031	7D4C	 	 	 
+FE4D3	\uDBB9\uDCD3	\xF3\xBE\x93\x93	NECKTIE		396	F366	EA93	7947	ED66	[ネクタイ]		272	F9A2	E302	7947	 	 	 
+FE4D4	\uDBB9\uDCD4	\xF3\xBE\x93\x94	LADIES HAT		407	F371	EA9E	7952	ED71	[帽子]		294	F9B8	E318	7952	 	 	 
+FE4D5	\uDBB9\uDCD5	\xF3\xBE\x93\x95	DRESS		793	F46F	EB6B	7B50	EE6F	[ドレス]		295	F9B9	E319	7B50	 	 	 
+FE4D6	\uDBB9\uDCD6	\xF3\xBE\x93\x96	HIGH HEELED SHOE		124	F6F3	E51A	7675	EBF3		55	F8D5	E674	7675		152	F77E	E13E	7675	 	 	
+FE4D7	\uDBB9\uDCD7	\xF3\xBE\x93\x97	MULE		124	F6F3	E51A	7675	EBF3		55	F8D5	E674	7675		296	F9BA	E31A	7D39	 	 	 
+FE4D8	\uDBB9\uDCD8	\xF3\xBE\x93\x98	LONG BOOTS		408	F372	EA9F	7953	ED72	[ブーツ]		297	F9BB	E31B	7953	 	 	 
+FE4D9	\uDBB9\uDCD9	\xF3\xBE\x93\x99	KIMONO		412	F376	EAA3	7957	ED76	[着物]		303	F9C1	E321	7957	 	 	 
+FE4DA	\uDBB9\uDCDA	\xF3\xBE\x93\x9A	BIKINI		413	F377	EAA4	7958	ED77	[ビキニ]		304	F9C2	E322	7958	 	 	 
+FE4DB	\uDBB9\uDCDB	\xF3\xBE\x93\x9B	WOMANS CLOTHES		301	F6E6	E50D	7668	EBE6		303	F9B3	E70E	7868		6	F946	E006	7868	 	 	 
+FE4DC	\uDBB9\uDCDC	\xF3\xBE\x93\x9C	PURSE		290	F6DD	E504	765F	EBDD		304	F9B4	E70F	765F	[財布]	 	 	
+FE4DD	\uDBB9\uDCDD	\xF3\xBE\x93\x9D	PRIZE BAG		233	F6A0	E4C7	7622	EBA0		310	F9BA	E715	7622		137	F76F	E12F	7622	 	 	
+FE4DE	\uDBB9\uDCDE	\xF3\xBE\x93\x9E	CURRENCY EXCHANGE	[$¥]	[$¥]		163	F78A	E149	7D5A	 	 	 
+FE4DF	\uDBB9\uDCDF	\xF3\xBE\x93\x9F	STOCK MARKET		373	F34F	E5DC	7930	ED4F	[株価]		164	F78B	E14A	7930	 	 	
+FE4E0	\uDBB9\uDCE0	\xF3\xBE\x93\xA0	DOLLAR SYMBOL		14	F796	E579	7776	EC96		310	F9BA	E715	7622		137	F76F	E12F	7622	 		 
+FE4E1	\uDBB9\uDCE1	\xF3\xBE\x93\xA1	CREDIT CARD		87	F799	E57C	7779	EC99	[カード]	[カード]	 	 	 
+FE4E2	\uDBB9\uDCE2	\xF3\xBE\x93\xA2	BILL WITH YEN SIGN		109	F79A	E57D	777A	EC9A		113	F97A	E6D6	777A	¥	 	 	
+FE4E3	\uDBB9\uDCE3	\xF3\xBE\x93\xA3	DOLLAR BILL		139	F7A2	E585	7824	ECA2		310	F9BA	E715	7622		137	F76F	E12F	7622	 	 	 
+FE4E4	\uDBB9\uDCE4	\xF3\xBE\x93\xA4	MONEY WITH WINGS		777	F45F	EB5B	7B40	EE5F	[飛んでいくお金]	[飛んでいくお金]	 	 	 
+FE4E5	\uDBB9\uDCE5	\xF3\xBE\x93\xA5	FLAG OF JAPAN		237	F6A5	E4CC	7627	EBA5	[日の丸]		434	FBAB	E50B	7627	 	 	 
+FE4E6	\uDBB9\uDCE6	\xF3\xBE\x93\xA6	FLAG OF THE USA		90	F790	E573	7770	EC90	[USA]		435	FBAC	E50C	7770	 	 	 
+FE4E7	\uDBB9\uDCE7	\xF3\xBE\x93\xA7	FLAG OF FRANCE		499	F3CE	EAFA	7A50	EDCE	[フランス]		436	FBAD	E50D	7A50	 	 	 
+FE4E8	\uDBB9\uDCE8	\xF3\xBE\x93\xA8	FLAG OF GERMANY		700	F3CF	EB0E	7A51	EDCF	[ドイツ]		437	FBAE	E50E	7A51	 	 	 
+FE4E9	\uDBB9\uDCE9	\xF3\xBE\x93\xA9	FLAG OF ITALY		701	F3D0	EB0F	7A52	EDD0	[イタリア]		438	FBAF	E50F	7A52	 	 	 
+FE4EA	\uDBB9\uDCEA	\xF3\xBE\x93\xAA	FLAG OF UNITED KINGDOM		702	F3D1	EB10	7A53	EDD1	[イギリス]		439	FBB0	E510	7A53	 	 	 
+FE4EB	\uDBB9\uDCEB	\xF3\xBE\x93\xAB	FLAG OF SPAIN		366	F348	E5D5	7929	ED48	[スペイン]		440	FBB1	E511	7929	 	 	 
+FE4EC	\uDBB9\uDCEC	\xF3\xBE\x93\xAC	FLAG OF RUSSIA		367	F349	E5D6	792A	ED49	[ロシア]		441	FBB2	E512	792A	 	 	 
+FE4ED	\uDBB9\uDCED	\xF3\xBE\x93\xAD	FLAG OF CHINA		703	F3D2	EB11	7A54	EDD2	[中国]		442	FBB3	E513	7A54	 	 	 
+FE4EE	\uDBB9\uDCEE	\xF3\xBE\x93\xAE	FLAG OF SOUTH KOREA		704	F3D3	EB12	7A55	EDD3	[韓国]		443	FBB4	E514	7A55	 	 	 
+FE4EF	\uDBB9\uDCEF	\xF3\xBE\x93\xAF	CAMERA		94	F6EE	E515	7670	EBEE		68	F8E2	E681	7670		8	F948	E008	7670		 	
+FE4F0	\uDBB9\uDCF0	\xF3\xBE\x93\xB0	HANDBAG		83	F674	E49C	7555	EB74		69	F8E3	E682	7555		305	F9C3	E323	7555	 	 	
+FE4F1	\uDBB9\uDCF1	\xF3\xBE\x93\xB1	POUCH	[ふくろ]		168	F951	E6AD	7E2B	[ふくろ]	 	 	
+FE4F2	\uDBB9\uDCF2	\xF3\xBE\x93\xB2	BELL		48	F6EB	E512	766D	EBEB		308	F9B8	E713	766D		307	F9C5	E325	766D			
+FE4F3	\uDBB9\uDCF3	\xF3\xBE\x93\xB3	DOOR	[ドア]		309	F9B9	E714	7E32	[ドア]	 	 	
+FE4F4	\uDBB9\uDCF4	\xF3\xBE\x93\xB4	POOP		283	F6CE	E4F5	7650	EBCE	[ウンチ]		90	F99B	E05A	7650			 
+FE4F5	\uDBB9\uDCF5	\xF3\xBE\x93\xB5	PISTOL		296	F6E3	E50A	7665	EBE3	[ピストル]		109	F753	E113	7665	 	 	 
+FE4F6	\uDBB9\uDCF6	\xF3\xBE\x93\xB6	FIRE		269	F653	E47B	7534	EB53	[炎]		119	F75D	E11D	7534	 		 
+FE4F7	\uDBB9\uDCF7	\xF3\xBE\x93\xB7	FORTUNE TELLING		392	F362	EA8F	7943	ED62	[占い]		242	F7DE	E23E	7943	 	 	 
+FE4F8	\uDBB9\uDCF8	\xF3\xBE\x93\xB8	CONSTELLATION		392	F362	EA8F	7943	ED62	[占い]		242	F7DE	E23E	7943	 	 	 
+FE4F9	\uDBB9\uDCF9	\xF3\xBE\x93\xB9	VIDEO CAMERA		111	F79B	E57E	777B	EC9B		58	F8D8	E677	7672		61	F97D	E03D	7672	 	 	 
+FE4FA	\uDBB9\uDCFA	\xF3\xBE\x93\xBA	KNIFE		114	F79C	E57F	777C	EC9C	[包丁]	[包丁]	 	 	 
+FE4FB	\uDBB9\uDCFB	\xF3\xBE\x93\xBB	FLASHLIGHT		130	F7A0	E583	7822	ECA0		151	F9A0	E6FB	752F	[懐中電灯]	 	 	 
+FE4FC	\uDBB9\uDCFC	\xF3\xBE\x93\xBC	BATTERY		135	F7A1	E584	7823	ECA1	[電池]	[電池]	 	 	 
+FE4FD	\uDBB9\uDCFD	\xF3\xBE\x93\xBD	SCROLL		136	F77B	E55F	775C	EC7B		166	F9AF	E70A	7A74	[スクロール]	 	 	 
+FE4FE	\uDBB9\uDCFE	\xF3\xBE\x93\xBE	ELECTRIC PLUG		162	F7A6	E589	7828	ECA6	[コンセント]	[コンセント]	 	 	 
+FE4FF	\uDBB9\uDCFF	\xF3\xBE\x93\xBF	BOOK 1		97	F782	E565	7762	EC82		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE500	\uDBB9\uDD00	\xF3\xBE\x94\x80	BOOK 2		100	F783	E566	7763	EC83		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE501	\uDBB9\uDD01	\xF3\xBE\x94\x81	BOOK 3		101	F784	E567	7764	EC84		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE502	\uDBB9\uDD02	\xF3\xBE\x94\x82	BOOK 4		102	F785	E568	7765	EC85		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE503	\uDBB9\uDD03	\xF3\xBE\x94\x83	BOOKS		147	F78C	E56F	776C	EC8C		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE504	\uDBB9\uDD04	\xF3\xBE\x94\x84	NAME PLATE		145	F6F6	E51D	7678	EBF6	[名札]	[名札]	 	 	 
+FE505	\uDBB9\uDD05	\xF3\xBE\x94\x85	BATH		369	F34B	E5D8	792C	ED4B		147	F99C	E6F7	7575		153	F780	E13F	792C	 	 	 
+FE506	\uDBB9\uDD06	\xF3\xBE\x94\x86	RESTROOM		207	F67D	E4A5	755E	EB7D		49	F8CF	E66E	755E		171	F792	E151	755E	 	 	
+FE507	\uDBB9\uDD07	\xF3\xBE\x94\x87	TOILET		207	F67D	E4A5	755E	EB7D		49	F8CF	E66E	755E		154	F781	E140	7D42	 	 	 
+FE508	\uDBB9\uDD08	\xF3\xBE\x94\x88	WATER CLOSET		207	F67D	E4A5	755E	EB7D		49	F8CF	E66E	755E		279	F9A9	E309	7D63	 	 	 
+FE509	\uDBB9\uDD09	\xF3\xBE\x94\x89	SYRINGE		304	F6E9	E510	766B	EBE9	[注射]		149	F77B	E13B	766B	 	 	 
+FE50A	\uDBB9\uDD0A	\xF3\xBE\x94\x8A	CAPSULE PILL		403	F36D	EA9A	794E	ED6D	[薬]		285	F9AF	E30F	794E	 	 	 
+FE50B	\uDBB9\uDD0B	\xF3\xBE\x94\x8B	BLOOD TYPE A		724	F3E7	EB26	7A69	EDE7	[A]		473	FBD2	E532	7A69	 	 	 
+FE50C	\uDBB9\uDD0C	\xF3\xBE\x94\x8C	BLOOD TYPE B		725	F3E8	EB27	7A6A	EDE8	[B]		474	FBD3	E533	7A6A	 	 	 
+FE50D	\uDBB9\uDD0D	\xF3\xBE\x94\x8D	BLOOD TYPE AB		727	F3EA	EB29	7A6C	EDEA	[AB]		475	FBD4	E534	7A6C	 	 	 
+FE50E	\uDBB9\uDD0E	\xF3\xBE\x94\x8E	BLOOD TYPE O		726	F3E9	EB28	7A6B	EDE9	[O]		476	FBD5	E535	7A6B	 	 	 
+FE50F	\uDBB9\uDD0F	\xF3\xBE\x94\x8F	RIBBON		312	F7BC	E59F	783E	ECBC		71	F8E5	E684	783E		290	F9B4	E314	783E	 	 	
+FE510	\uDBB9\uDD10	\xF3\xBE\x94\x90	PRESENT		144	F6A8	E4CF	762A	EBA8		72	F8E6	E685	762A		108	F752	E112	762A		 	
+FE511	\uDBB9\uDD11	\xF3\xBE\x94\x91	BIRTHDAY CAKE		313	F7BD	E5A0	783F	ECBD		73	F8E7	E686	783F		345	F9EB	E34B	783F	 	 	
+FE512	\uDBB9\uDD12	\xF3\xBE\x94\x92	CHRISTMAS TREE		234	F6A2	E4C9	7624	EBA2		103	F948	E6A4	7624		51	F973	E033	7624	 	 	
+FE513	\uDBB9\uDD13	\xF3\xBE\x94\x93	SANTA CLAUS		489	F3C4	EAF0	7A46	EDC4	[サンタ]		419	FB89	E448	7A46	 	 	
+FE514	\uDBB9\uDD14	\xF3\xBE\x94\x94	TWO JAPANESE FLAGS		370	F34C	E5D9	792D	ED4C	[祝日]		157	F784	E143	792D	 	 	 
+FE515	\uDBB9\uDD15	\xF3\xBE\x94\x95	FIREWORKS		357	F7FC	E5CC	787E	ECFC	[花火]		113	F757	E117	787E	 	 	 
+FE516	\uDBB9\uDD16	\xF3\xBE\x94\x96	BALLOON		404	F36E	EA9B	794F	ED6E	[風船]		286	F9B0	E310	794F	 	 	 
+FE517	\uDBB9\uDD17	\xF3\xBE\x94\x97	PARTY POPPER		405	F36F	EA9C	7950	ED6F	[クラッカー]		288	F9B2	E312	7950		 	 
+FE518	\uDBB9\uDD18	\xF3\xBE\x94\x98	PINE DECORATION		476	F3B7	EAE3	7A39	EDB7	[門松]		401	FB76	E436	7A39	 	 	 
+FE519	\uDBB9\uDD19	\xF3\xBE\x94\x99	GIRLS DOLLS FESTIVAL		477	F3B8	EAE4	7A3A	EDB8	[ひな祭り]		403	FB78	E438	7A3A	 	 	 
+FE51A	\uDBB9\uDD1A	\xF3\xBE\x94\x9A	GRADUATION CAP		478	F3B9	EAE5	7A3B	EDB9	[卒業式]		404	FB79	E439	7A3B	 	 	 
+FE51B	\uDBB9\uDD1B	\xF3\xBE\x94\x9B	SCHOOL SATCHEL		479	F3BA	EAE6	7A3C	EDBA	[ランドセル]		405	FB7A	E43A	7A3C	 	 	 
+FE51C	\uDBB9\uDD1C	\xF3\xBE\x94\x9C	CARP STREAMER		480	F3BB	EAE7	7A3D	EDBB	[こいのぼり]		406	FB7B	E43B	7A3D	 	 	 
+FE51D	\uDBB9\uDD1D	\xF3\xBE\x94\x9D	JAPANESE SPARKLER		484	F3BF	EAEB	7A41	EDBF	[線香花火]		411	FB81	E440	7A41	 	 	 
+FE51E	\uDBB9\uDD1E	\xF3\xBE\x94\x9E	WIND CHIME		486	F3C1	EAED	7A43	EDC1	[風鈴]		413	FB83	E442	7A43	 	 	 
+FE51F	\uDBB9\uDD1F	\xF3\xBE\x94\x9F	JACK O LANTERN		487	F3C2	EAEE	7A44	EDC2	[ハロウィン]		416	FB86	E445	7A44	 	 	 
+FE520	\uDBB9\uDD20	\xF3\xBE\x94\xA0	DECORATIVE CONFETTI BALL		230	F647	E46F	7528	EB47	[オメデトウ]	[オメデトウ]	 	 	 
+FE521	\uDBB9\uDD21	\xF3\xBE\x94\xA1	TANABATA TREE		747	F441	EB3D	7B22	EE41	[七夕]	[七夕]	 	 	 
+FE522	\uDBB9\uDD22	\xF3\xBE\x94\xA2	PAGER		308	F7B8	E59B	783A	ECB8		29	F8BB	E65A	783A	[ポケベル]	 	 	
+FE523	\uDBB9\uDD23	\xF3\xBE\x94\xA3	TELEPHONE		85	F7B3	E596	7835	ECB3		74	F8E8	E687	7835		9	F949	E009	7835	 	 	
+FE524	\uDBB9\uDD24	\xF3\xBE\x94\xA4	TELEPHONE 2		155	F6F7	E51E	7679	EBF7		74	F8E8	E687	7835		9	F949	E009	7835	 	 	 
+FE525	\uDBB9\uDD25	\xF3\xBE\x94\xA5	MOBILE PHONE		161	F7A5	E588	7827	ECA5		75	F8E9	E688	7827		10	F94A	E00A	7827		 	
+FE526	\uDBB9\uDD26	\xF3\xBE\x94\xA6	PHONE WITH ARROW		513	F7DF	EB08	7861	ECDF		105	F972	E6CE	7861		94	F744	E104	7861	 	 	
+FE527	\uDBB9\uDD27	\xF3\xBE\x94\xA7	MEMO		395	F365	EA92	7946	ED65		76	F8EA	E689	7946		271	F9A1	E301	7946	 	 	
+FE528	\uDBB9\uDD28	\xF3\xBE\x94\xA8	FAX		166	F6F9	E520	767B	EBF9		107	F974	E6D0	767B		11	F94B	E00B	767B	 	 	
+FE529	\uDBB9\uDD29	\xF3\xBE\x94\xA9	ENVELOPE		108	F6FA	E521	767C	EBFA		110	F977	E6D3	767C		93	F743	E103	7B47	 	 	
+FE52A	\uDBB9\uDD2A	\xF3\xBE\x94\xAA	ENVELOPE 2		151	F7AE	E591	7830	ECAE		106	F973	E6CF	7B47		93	F743	E103	7B47	 	 	 
+FE52B	\uDBB9\uDD2B	\xF3\xBE\x94\xAB	ENVELOPE WITH ARROW		784	F466	EB62	7B47	EE66		106	F973	E6CF	7B47		93	F743	E103	7B47	 	 	
+FE52C	\uDBB9\uDD2C	\xF3\xBE\x94\xAC	MAILBOX		129	F6F4	E51B	7676	EBF4		40	F8C6	E665	7932		91	F741	E101	7D3C	 	 	 
+FE52D	\uDBB9\uDD2D	\xF3\xBE\x94\xAD	MAILBOX 2		515	F7E1	EB0A	7863	ECE1		40	F8C6	E665	7932		91	F741	E101	7D3C	 	 	 
+FE52E	\uDBB9\uDD2E	\xF3\xBE\x94\xAE	POSTBOX		129	F6F4	E51B	7676	EBF4		40	F8C6	E665	7932		92	F742	E102	7676	 	 	 
+FE52F	\uDBB9\uDD2F	\xF3\xBE\x94\xAF	MEGAPHONE		13	F6EA	E511	766C	EBEA	[スピーカ]		156	F783	E142	7D24	 	 	 
+FE530	\uDBB9\uDD30	\xF3\xBE\x94\xB0	LOUD SPEAKER		13	F6EA	E511	766C	EBEA	[スピーカ]		293	F9B7	E317	766C	 	 	 
+FE531	\uDBB9\uDD31	\xF3\xBE\x94\xB1	SATELLITE ANTENNA		210	F681	E4A8	7561	EB81	[アンテナ]		165	F78C	E14B	7561	 	 	 
+FE532	\uDBB9\uDD32	\xF3\xBE\x94\xB2	DIALOG BUBBLE		86	F6D6	E4FD	7658	EBD6	[フキダシ]	[フキダシ]	 	 	 
+FE533	\uDBB9\uDD33	\xF3\xBE\x94\xB3	OUTBOX TRAY		153	F7AF	E592	7831	ECAF	[送信BOX]	[送信BOX]	 	 	 
+FE534	\uDBB9\uDD34	\xF3\xBE\x94\xB4	INBOX TRAY		154	F7B0	E593	7832	ECB0	[受信BOX]	[受信BOX]	 	 	 
+FE535	\uDBB9\uDD35	\xF3\xBE\x94\xB5	PACKAGE		165	F6F8	E51F	767A	EBF8		72	F8E6	E685	762A		108	F752	E112	762A	 	 	 
+FE536	\uDBB9\uDD36	\xF3\xBE\x94\xB6	BLACK NIB		508	F7DA	EB03	785C	ECDA		169	F952	E6AE	785C	[ペン]	 	 	
+FE537	\uDBB9\uDD37	\xF3\xBE\x94\xB7	CHAIR	[いす]		171	F956	E6B2	7D57		121	F75F	E11F	7D56	 	 	
+FE538	\uDBB9\uDD38	\xF3\xBE\x94\xB8	PC		337	F7E8	E5B8	786A	ECE8		311	F9BB	E716	786A		12	F94C	E00C	786A		 	
+FE539	\uDBB9\uDD39	\xF3\xBE\x94\xB9	PENCIL		149	F679	E4A1	755A	EB79		314	F9BE	E719	755A		271	F9A1	E301	7946	 	 	
+FE53A	\uDBB9\uDD3A	\xF3\xBE\x94\xBA	PAPERCLIP		143	F678	E4A0	7559	EB78		337	F9D5	E730	7559	[クリップ]	 	 	
+FE53B	\uDBB9\uDD3B	\xF3\xBE\x94\xBB	BRIEFCASE		359	F341	E5CE	7922	ED41		69	F8E3	E682	7555		120	F75E	E11E	7922		 	 
+FE53C	\uDBB9\uDD3C	\xF3\xBE\x94\xBC	FLOPPY DISK		126	F79F	E582	7821	EC9F	[フロッピー]		292	F9B6	E316	7821	 	 	 
+FE53D	\uDBB9\uDD3D	\xF3\xBE\x94\xBD	FLOPPY DISK 2		59	F77E	E562	775F	EC7E	[フロッピー]		292	F9B6	E316	7821	 	 	 
+FE53E	\uDBB9\uDD3E	\xF3\xBE\x94\xBE	BLACK SCISSORS		104	F6EF	E516	7671	EBEF		56	F8D6	E675	7671		289	F9B3	E313	7671	 	 	
+FE53F	\uDBB9\uDD3F	\xF3\xBE\x94\xBF	PUSHPIN		49	F77C	E560	775D	EC7C	φ	φ	 	 	 
+FE540	\uDBB9\uDD40	\xF3\xBE\x95\x80	DOCUMENT		56	F77D	E561	775E	EC7D		76	F8EA	E689	7946		271	F9A1	E301	7946	 	 	 
+FE541	\uDBB9\uDD41	\xF3\xBE\x95\x81	DOCUMENT 2		103	F786	E569	7766	EC86		76	F8EA	E689	7946		271	F9A1	E301	7946	 	 	 
+FE542	\uDBB9\uDD42	\xF3\xBE\x95\x82	CALENDAR		67	F780	E563	7760	EC80	[カレンダー]	[カレンダー]	 	 	 
+FE543	\uDBB9\uDD43	\xF3\xBE\x95\x83	FILE FOLDER		79	F7AC	E58F	782E	ECAC	[フォルダ]	[フォルダ]	 	 	 
+FE544	\uDBB9\uDD44	\xF3\xBE\x95\x84	FILE FOLDER 2		84	F7AD	E590	782F	ECAD	[フォルダ]	[フォルダ]	 	 	 
+FE545	\uDBB9\uDD45	\xF3\xBE\x95\x85	NOTEBOOK		121	F788	E56B	7768	EC88		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE546	\uDBB9\uDD46	\xF3\xBE\x95\x86	NOTEBOOK 2		122	F677	E49F	7558	EB77		70	F8E4	E683	7558		162	F789	E148	7558	 	 	
+FE547	\uDBB9\uDD47	\xF3\xBE\x95\x87	NOTEBOOK 3		91	F675	E49D	7556	EB75		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE548	\uDBB9\uDD48	\xF3\xBE\x95\x88	CLIPBOARD		92	F781	E564	7761	EC81		76	F8EA	E689	7946		271	F9A1	E301	7946	 	 	 
+FE549	\uDBB9\uDD49	\xF3\xBE\x95\x89	TEAR OFF CALENDAR		105	F787	E56A	7767	EC87	[カレンダー]	[カレンダー]	 	 	 
+FE54A	\uDBB9\uDD4A	\xF3\xBE\x95\x8A	CHART		127	F791	E574	7771	EC91	[グラフ]		164	F78B	E14A	7930	 	 	 
+FE54B	\uDBB9\uDD4B	\xF3\xBE\x95\x8B	LINE GRAPH CHART 1		128	F792	E575	7772	EC92	[グラフ]		164	F78B	E14A	7930	 	 	 
+FE54C	\uDBB9\uDD4C	\xF3\xBE\x95\x8C	LINE GRAPH CHART 2		159	F793	E576	7773	EC93	[グラフ]	[グラフ]	 	 	 
+FE54D	\uDBB9\uDD4D	\xF3\xBE\x95\x8D	ROLODEX		131	F789	E56C	7769	EC89		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE54E	\uDBB9\uDD4E	\xF3\xBE\x95\x8E	THUMBTACK		137	F78A	E56D	776A	EC8A	[画びょう]	[画びょう]	 	 	 
+FE54F	\uDBB9\uDD4F	\xF3\xBE\x95\x8F	LEDGER		142	F78B	E56E	776B	EC8B		70	F8E4	E683	7558		162	F789	E148	7558	 	 	 
+FE550	\uDBB9\uDD50	\xF3\xBE\x95\x90	RULER 1		157	F78D	E570	776D	EC8D	[定規]	[定規]	 	 	 
+FE551	\uDBB9\uDD51	\xF3\xBE\x95\x91	RULER 2		158	F67A	E4A2	755B	EB7A	[三角定規]	[三角定規]	 	 	 
+FE552	\uDBB9\uDD52	\xF3\xBE\x95\x92	BOOKMARK TABS		516	F7E2	EB0B	7864	ECE2		76	F8EA	E689	7946		271	F9A1	E301	7946	 	 	 
+FE553	\uDBB9\uDD53	\xF3\xBE\x95\x93	FOOT PRINTS		728	F3EB	EB2A	7A6D	EDEB		91	F8F9	E698	7A6D		477	FBD6	E536	7A6D	 	 	
+FE7D0	\uDBB9\uDFD0	\xF3\xBE\x9F\x90	RUNNING SHIRT WITH SASH	〓		21	F8B3	E652	7E21	〓	 	 	
+FE7D1	\uDBB9\uDFD1	\xF3\xBE\x9F\x91	BASEBALL		45	F693	E4BA	7573	EB93		22	F8B4	E653	7573		22	F956	E016	7573		 	
+FE7D2	\uDBB9\uDFD2	\xF3\xBE\x9F\x92	GOLF		306	F7B6	E599	7838	ECB6		23	F8B5	E654	7838		20	F954	E014	7838	 	 	
+FE7D3	\uDBB9\uDFD3	\xF3\xBE\x9F\x93	TENNIS		220	F690	E4B7	7570	EB90		24	F8B6	E655	7570		21	F955	E015	7570	 	 	
+FE7D4	\uDBB9\uDFD4	\xF3\xBE\x9F\x94	SOCCER		219	F68F	E4B6	756F	EB8F		25	F8B7	E656	756F		24	F958	E018	756F	 	 	
+FE7D5	\uDBB9\uDFD5	\xF3\xBE\x9F\x95	SKI		421	F380	EAAC	7960	ED80		26	F8B8	E657	7960		19	F953	E013	7960	 	 	
+FE7D6	\uDBB9\uDFD6	\xF3\xBE\x9F\x96	BASKETBALL		307	F7B7	E59A	7839	ECB7		27	F8B9	E658	7839		389	FB6A	E42A	7839	 	 	
+FE7D7	\uDBB9\uDFD7	\xF3\xBE\x9F\x97	CHECKERED FLAG		222	F692	E4B9	7572	EB92		28	F8BA	E659	7572		140	F772	E132	7572	 	 	
+FE7D8	\uDBB9\uDFD8	\xF3\xBE\x9F\x98	SNOWBOARDER		221	F691	E4B8	7571	EB91		307	F9B7	E712	7571	[スノボ]	 	 	
+FE7D9	\uDBB9\uDFD9	\xF3\xBE\x9F\x99	RUNNER		218	F643	E46B	7524	EB43		340	F9D8	E733	7524		111	F755	E115	7524	 	 	
+FE7DA	\uDBB9\uDFDA	\xF3\xBE\x9F\x9A	SURFER		751	F445	EB41	7B26	EE45		307	F9B7	E712	7571		23	F957	E017	7B26	 	 	 
+FE7DB	\uDBB9\uDFDB	\xF3\xBE\x9F\x9B	TROPHY		364	F346	E5D3	7927	ED46	[トロフィー]		139	F771	E131	7927	 	 	 
+FE7DC	\uDBB9\uDFDC	\xF3\xBE\x9F\x9C	HORSE RACING		248	F6B1	E4D8	7633	EBB1		373	F9F9	E754	7633		142	F774	E134	7D44	 	 	 
+FE7DD	\uDBB9\uDFDD	\xF3\xBE\x9F\x9D	FOOTBALL		96	F694	E4BB	7574	EB94	[フットボール]		390	FB6B	E42B	7574	 	 	 
+FE7DE	\uDBB9\uDFDE	\xF3\xBE\x9F\x9E	SWIMMER		471	F3B2	EADE	7A34	EDB2	[水泳]		392	FB6D	E42D	7A34	 	 	 
+FE7DF	\uDBB9\uDFDF	\xF3\xBE\x9F\x9F	TRAIN		172	F68E	E4B5	756E	EB8E		30	F8BC	E65B	756E		30	F95E	E01E	756E	 	 	
+FE7E0	\uDBB9\uDFE0	\xF3\xBE\x9F\xA0	SUBWAY		341	F7EC	E5BC	786E	ECEC		31	F8BD	E65C	786E		399	FB74	E434	786E	 	 	 
+FE7E1	\uDBB9\uDFE1	\xF3\xBE\x9F\xA1	METRO SIGN		341	F7EC	E5BC	786E	ECEC		31	F8BD	E65C	786E		399	FB74	E434	786E	 	 	
+FE7E2	\uDBB9\uDFE2	\xF3\xBE\x9F\xA2	BULLET TRAIN		217	F689	E4B0	7569	EB89		32	F8BE	E65D	7569		400	FB75	E435	7569	 	 	
+FE7E3	\uDBB9\uDFE3	\xF3\xBE\x9F\xA3	BULLET TRAIN 2		217	F689	E4B0	7569	EB89		32	F8BE	E65D	7569		31	F95F	E01F	7D43	 	 	 
+FE7E4	\uDBB9\uDFE4	\xF3\xBE\x9F\xA4	CAR		125	F68A	E4B1	756A	EB8A		33	F8BF	E65E	756A		27	F95B	E01B	756A		 	
+FE7E5	\uDBB9\uDFE5	\xF3\xBE\x9F\xA5	CAR 2		125	F68A	E4B1	756A	EB8A		34	F8C0	E65F	7E22		393	FB6E	E42E	7D3B	 	 	
+FE7E6	\uDBB9\uDFE6	\xF3\xBE\x9F\xA6	BUS		216	F688	E4AF	7568	EB88		35	F8C1	E660	7568		179	F79A	E159	7568	 	 	
+FE7E7	\uDBB9\uDFE7	\xF3\xBE\x9F\xA7	BUS STOP		209	F680	E4A7	7560	EB80	[バス停]		170	F791	E150	7560	 	 	 
+FE7E8	\uDBB9\uDFE8	\xF3\xBE\x9F\xA8	SHIP		379	F355	EA82	7936	ED55		36	F8C2	E661	7936		182	F7A2	E202	7936	 	 	
+FE7E9	\uDBB9\uDFE9	\xF3\xBE\x9F\xA9	AIRPLANE		168	F68C	E4B3	756C	EB8C		37	F8C3	E662	756C		29	F95D	E01D	756C	 	 	
+FE7EA	\uDBB9\uDFEA	\xF3\xBE\x9F\xAA	SAILBOAT		169	F68D	E4B4	756D	EB8D		102	F947	E6A3	756D		28	F95C	E01C	756D	 	 	
+FE7EB	\uDBB9\uDFEB	\xF3\xBE\x9F\xAB	BICYCLE		215	F687	E4AE	7567	EB87		318	F9C2	E71D	7567		144	F776	E136	7567	 	 	
+FE7EC	\uDBB9\uDFEC	\xF3\xBE\x9F\xAC	STATION		795	F471	EB6D	7B52	EE71	[駅]		57	F979	E039	7B52	 	 	 
+FE7ED	\uDBB9\uDFED	\xF3\xBE\x9F\xAD	ROCKET		353	F7F8	E5C8	787A	ECF8	[ロケット]		103	F74D	E10D	787A	 	 	 
+FE7EE	\uDBB9\uDFEE	\xF3\xBE\x9F\xAE	SPEEDBOAT		169	F68D	E4B4	756D	EB8D		102	F947	E6A3	756D		143	F775	E135	7D3F	 	 	 
+FE7EF	\uDBB9\uDFEF	\xF3\xBE\x9F\xAF	TAXI		125	F68A	E4B1	756A	EB8A		33	F8BF	E65E	756A		180	F79B	E15A	7D3A	 	 	 
+FE7F0	\uDBB9\uDFF0	\xF3\xBE\x9F\xB0	WALKER		800	F476	EB72	7B57	EE76		340	F9D8	E733	7524		181	F7A1	E201	7B57	 	 	 
+FE7F1	\uDBB9\uDFF1	\xF3\xBE\x9F\xB1	TRUCK		148	F68B	E4B2	756B	EB8B	[トラック]		394	FB6F	E42F	756B	 	 	 
+FE7F2	\uDBB9\uDFF2	\xF3\xBE\x9F\xB2	FIRE ENGINE		472	F3B3	EADF	7A35	EDB3	[消防車]		395	FB70	E430	7A35	 	 	 
+FE7F3	\uDBB9\uDFF3	\xF3\xBE\x9F\xB3	AMBULANCE		473	F3B4	EAE0	7A36	EDB4	[救急車]		396	FB71	E431	7A36	 	 	 
+FE7F4	\uDBB9\uDFF4	\xF3\xBE\x9F\xB4	PATROL CAR		474	F3B5	EAE1	7A37	EDB5	[パトカー]		397	FB72	E432	7A37	 	 	 
+FE7F5	\uDBB9\uDFF5	\xF3\xBE\x9F\xB5	GAS STATION		213	F78E	E571	776E	EC8E		46	F8CC	E66B	776E		58	F97A	E03A	776E	 	 	
+FE7F6	\uDBB9\uDFF6	\xF3\xBE\x9F\xB6	PARKING		208	F67E	E4A6	755F	EB7E		47	F8CD	E66C	755F		169	F790	E14F	755F	 	 	
+FE7F7	\uDBB9\uDFF7	\xF3\xBE\x9F\xB7	TRAFFIC LIGHT		99	F642	E46A	7523	EB42		48	F8CE	E66D	7523		168	F78F	E14E	7523	 	 	
+FE7F8	\uDBB9\uDFF8	\xF3\xBE\x9F\xB8	CONSTRUCTION		368	F34A	E5D7	792B	ED4A	[工事中]		145	F777	E137	792B	 	 	 
+FE7F9	\uDBB9\uDFF9	\xF3\xBE\x9F\xB9	PATROL CARS REVOLVING LIGHT		801	F477	EB73	7B58	EE77	[パトカー]		397	FB72	E432	7A37	 	 	 
+FE7FA	\uDBB9\uDFFA	\xF3\xBE\x9F\xBA	HOT SPRING		224	F695	E4BC	7575	EB95		147	F99C	E6F7	7575		125	F763	E123	7575	 	 	
+FE7FB	\uDBB9\uDFFB	\xF3\xBE\x9F\xBB	CAMP		361	F343	E5D0	7924	ED43	[キャンプ]		124	F762	E122	7924	 	 	 
+FE7FC	\uDBB9\uDFFC	\xF3\xBE\x9F\xBC	AMUSEMENT PARK	〓		60	F8DA	E679	7E23	〓	 	 	
+FE7FD	\uDBB9\uDFFD	\xF3\xBE\x9F\xBD	AMUSEMENT PARK 2		223	F645	E46D	7526	EB45	[観覧車]		126	F764	E124	7526	 	 	 
+FE7FE	\uDBB9\uDFFE	\xF3\xBE\x9F\xBE	ROLLER COASTER		475	F3B6	EAE2	7A38	EDB6	[ジェットコースター]		398	FB73	E433	7A38	 	 	 
+FE7FF	\uDBB9\uDFFF	\xF3\xBE\x9F\xBF	FISHING		752	F446	EB42	7B27	EE46		370	F9F6	E751	7A60		25	F959	E019	7D41	 	 	 
+FE800	\uDBBA\uDC00	\xF3\xBE\xA0\x80	MICROPHONE		289	F6DC	E503	765E	EBDC		57	F8D7	E676	765E		60	F97C	E03C	765E		 	
+FE801	\uDBBA\uDC01	\xF3\xBE\xA0\x81	MOVIES		110	F6F0	E517	7672	EBF0		58	F8D8	E677	7672		61	F97D	E03D	7672		 	
+FE802	\uDBBA\uDC02	\xF3\xBE\xA0\x82	MOVIE THEATER		110	F6F0	E517	7672	EBF0		58	F8D8	E677	7672		430	FBA7	E507	7D38	 	 	 
+FE803	\uDBBA\uDC03	\xF3\xBE\xA0\x83	HEADPHONE		294	F6E1	E508	7663	EBE1		61	F8DB	E67A	7663		280	F9AA	E30A	7663	 	 	
+FE804	\uDBBA\uDC04	\xF3\xBE\xA0\x84	ART		309	F7B9	E59C	783B	ECB9		62	F8DC	E67B	783B		425	FBA2	E502	783B	 	 	
+FE805	\uDBBA\uDC05	\xF3\xBE\xA0\x85	BLACK SILK HAT		494	F3C9	EAF5	7A4B	EDC9		63	F8DD	E67C	7A4B		426	FBA3	E503	7A4B	 	 	
+FE806	\uDBBA\uDC06	\xF3\xBE\xA0\x86	EVENT		311	F7BB	E59E	783D	ECBB		64	F8DE	E67D	783D	[イベント]	 	 	
+FE807	\uDBBA\uDC07	\xF3\xBE\xA0\x87	TICKET		106	F676	E49E	7557	EB76		65	F8DF	E67E	7557		127	F765	E125	7557	 	 	
+FE808	\uDBBA\uDC08	\xF3\xBE\xA0\x88	MOVIE SHOOTING CLAPBOARD		226	F697	E4BE	7577	EB97		167	F950	E6AC	7577		306	F9C4	E324	7577	 	 	
+FE809	\uDBBA\uDC09	\xF3\xBE\xA0\x89	PERFORMING ARTS		310	F7BA	E59D	783C	ECBA	[演劇]		426	FBA3	E503	7A4B	 	 	 
+FE80A	\uDBBA\uDC0A	\xF3\xBE\xA0\x8A	VIDEO GAME		232	F69F	E4C6	7621	EB9F		78	F8EC	E68B	7621	[ゲーム]	 	 	
+FE80B	\uDBBA\uDC0B	\xF3\xBE\xA0\x8B	MAHJONG		362	F344	E5D1	7925	ED44	[麻雀]		135	F76D	E12D	7925	 	 	
+FE80C	\uDBBA\uDC0C	\xF3\xBE\xA0\x8C	BULLS EYE		231	F69E	E4C5	757E	EB9E	[的中]		138	F770	E130	757E	 	 	 
+FE80D	\uDBBA\uDC0D	\xF3\xBE\xA0\x8D	SLOT MACHINE		229	F646	E46E	7527	EB46	[777]		141	F773	E133	7527	 	 	 
+FE80E	\uDBBA\uDC0E	\xF3\xBE\xA0\x8E	BILLIARDS		470	F3B1	EADD	7A33	EDB1	[ビリヤード]		391	FB6C	E42C	7A33	 	 	 
+FE80F	\uDBBA\uDC0F	\xF3\xBE\xA0\x8F	A DIE		170	F6A1	E4C8	7623	EBA1	[サイコロ]	[サイコロ]	 	 	 
+FE810	\uDBBA\uDC10	\xF3\xBE\xA0\x90	BOWLING		753	F447	EB43	7B28	EE47	[ボーリング]	[ボーリング]	 	 	 
+FE811	\uDBBA\uDC11	\xF3\xBE\xA0\x91	JAPANESE FLOWER PLAYING CARDS		796	F472	EB6E	7B53	EE72	[花札]	[花札]	 	 	 
+FE812	\uDBBA\uDC12	\xF3\xBE\xA0\x92	JOKER CARD		797	F473	EB6F	7B54	EE73	[ジョーカー]	[ジョーカー]	 	 	 
+FE813	\uDBBA\uDC13	\xF3\xBE\xA0\x93	EIGHTH NOTE		343	F7EE	E5BE	7870	ECEE		146	F99B	E6F6	7870		62	F97E	E03E	7870		 	
+FE814	\uDBBA\uDC14	\xF3\xBE\xA0\x94	MULTIPLE MUSICAL NOTES		291	F6DE	E505	7660	EBDE		155	F9A4	E6FF	7660		308	F9C6	E326	7660		 	
+FE815	\uDBBA\uDC15	\xF3\xBE\xA0\x95	SAXOPHONE	〓	〓		64	F981	E040	7D55	 	 	 
+FE816	\uDBBA\uDC16	\xF3\xBE\xA0\x96	GUITAR		292	F6DF	E506	7661	EBDF	[ギター]		65	F982	E041	7661	 	 	
+FE817	\uDBBA\uDC17	\xF3\xBE\xA0\x97	PIANO		750	F444	EB40	7B25	EE44	[ピアノ]	[ピアノ]	 	 	 
+FE818	\uDBBA\uDC18	\xF3\xBE\xA0\x98	TRUMPET		469	F3B0	EADC	7A32	EDB0	[トランペット]		66	F983	E042	7A32	 	 	 
+FE819	\uDBBA\uDC19	\xF3\xBE\xA0\x99	VIOLIN		293	F6E0	E507	7662	EBE0	[バイオリン]	[バイオリン]	 	 	 
+FE81A	\uDBBA\uDC1A	\xF3\xBE\xA0\x9A	MUSICAL SCORE		453	F3A0	EACC	7A22	EDA0		155	F9A4	E6FF	7660		308	F9C6	E326	7660	 	 	 
+FE81B	\uDBBA\uDC1B	\xF3\xBE\xA0\x9B	START SINGING SYMBOL	[グラフ]	[グラフ]		134	F76C	E12C	7D57	 	 	 
+FE81C	\uDBBA\uDC1C	\xF3\xBE\xA0\x9C	TV		288	F6DB	E502	765D	EBDB		77	F8EB	E68A	765D		132	F76A	E12A	765D		 	
+FE81D	\uDBBA\uDC1D	\xF3\xBE\xA0\x9D	CD		300	F6E5	E50C	7667	EBE5		79	F8ED	E68C	7667		128	F766	E126	7667	 	 	
+FE81E	\uDBBA\uDC1E	\xF3\xBE\xA0\x9E	DVD		300	F6E5	E50C	7667	EBE5		79	F8ED	E68C	7667		129	F767	E127	7D49	 	 	 
+FE81F	\uDBBA\uDC1F	\xF3\xBE\xA0\x9F	RADIO		338	F7E9	E5B9	786B	ECE9	[ラジオ]		130	F768	E128	786B		 	 
+FE820	\uDBBA\uDC20	\xF3\xBE\xA0\xA0	VIDEO TAPE		115	F79D	E580	777D	EC9D	[ビデオ]		131	F769	E129	777D	 	 	 
+FE821	\uDBBA\uDC21	\xF3\xBE\xA0\xA1	SPEAKER		13	F6EA	E511	766C	EBEA	[スピーカ]		155	F782	E141	7D23	 	 	 
+FE822	\uDBBA\uDC22	\xF3\xBE\xA0\xA2	NEWSPAPER		171	F7A8	E58B	782A	ECA8	[新聞]	[新聞]	 	 	
+FE823	\uDBBA\uDC23	\xF3\xBE\xA0\xA3	KISS MARK		273	F6C4	E4EB	7646	EBC4		149	F99E	E6F9	7646		3	F943	E003	7646	 	 	
+FE824	\uDBBA\uDC24	\xF3\xBE\xA0\xA4	LOVE LETTER		806	F47C	EB78	7B5D	EE7C		312	F9BC	E717	7B5D		93+310	F743+F9C8	E103+E328	7B47+7B5A	 	 	
+FE825	\uDBBA\uDC25	\xF3\xBE\xA0\xA5	RING		72	F6ED	E514	766F	EBED		316	F9C0	E71B	766F		52	F974	E034	766F	 	 	
+FE826	\uDBBA\uDC26	\xF3\xBE\xA0\xA6	GEM STONE		72	F6ED	E514	766F	EBED		316	F9C0	E71B	766F		53	F975	E035	7D35	 	 	 
+FE827	\uDBBA\uDC27	\xF3\xBE\xA0\xA7	KISS		355	F7FA	E5CA	787C	ECFA		149	F99E	E6F9	7646		107	F751	E111	787C	 		 
+FE828	\uDBBA\uDC28	\xF3\xBE\xA0\xA8	BOUQUET		398	F368	EA95	7949	ED68	[花束]		276	F9A6	E306	7949	 	 	 
+FE829	\uDBBA\uDC29	\xF3\xBE\xA0\xA9	COUPLE		467	F3AE	EADA	7A30	EDAE		137	F992	E6ED	7B5A		384	FB65	E425	7A30	 	 	
+FE82A	\uDBBA\uDC2A	\xF3\xBE\xA0\xAA	WEDDING		340	F7EB	E5BB	786D	ECEB	〓		408	FB7D	E43D	7D4A	 	 	 
+FE82B	\uDBBA\uDC2B	\xF3\xBE\xA0\xAB	FREE DIAL	[フリーダイヤル]		122	F984	E6DF	7D5D		197	F7B1	E211	7D5C	 	 	
+FE82C	\uDBBA\uDC2C	\xF3\xBE\xA0\xAC	HASH KEY		818	F489	EB84	7B69	EE89		123	F985	E6E0	7B69		196	F7B0	E210	7B69	 	 	
+FE82D	\uDBBA\uDC2D	\xF3\xBE\xA0\xAD	MOBILE Q		4	F748	E52C	7729	EC48		124	F986	E6E1	7729	[Q]	 	 	
+FE82E	\uDBBA\uDC2E	\xF3\xBE\xA0\xAE	KEYPAD 1		180	F6FB	E522	767D	EBFB		125	F987	E6E2	767D		208	F7BC	E21C	767D	 	 	
+FE82F	\uDBBA\uDC2F	\xF3\xBE\xA0\xAF	KEYPAD 2		181	F6FC	E523	767E	EBFC		126	F988	E6E3	767E		209	F7BD	E21D	767E	 	 	
+FE830	\uDBBA\uDC30	\xF3\xBE\xA0\xB0	KEYPAD 3		182	F740	E524	7721	EC40		127	F989	E6E4	7721		210	F7BE	E21E	7721	 	 	
+FE831	\uDBBA\uDC31	\xF3\xBE\xA0\xB1	KEYPAD 4		183	F741	E525	7722	EC41		128	F98A	E6E5	7722		211	F7BF	E21F	7722	 	 	
+FE832	\uDBBA\uDC32	\xF3\xBE\xA0\xB2	KEYPAD 5		184	F742	E526	7723	EC42		129	F98B	E6E6	7723		212	F7C0	E220	7723	 	 	
+FE833	\uDBBA\uDC33	\xF3\xBE\xA0\xB3	KEYPAD 6		185	F743	E527	7724	EC43		130	F98C	E6E7	7724		213	F7C1	E221	7724	 	 	
+FE834	\uDBBA\uDC34	\xF3\xBE\xA0\xB4	KEYPAD 7		186	F744	E528	7725	EC44		131	F98D	E6E8	7725		214	F7C2	E222	7725	 	 	
+FE835	\uDBBA\uDC35	\xF3\xBE\xA0\xB5	KEYPAD 8		187	F745	E529	7726	EC45		132	F98E	E6E9	7726		215	F7C3	E223	7726	 	 	
+FE836	\uDBBA\uDC36	\xF3\xBE\xA0\xB6	KEYPAD 9		188	F746	E52A	7727	EC46		133	F98F	E6EA	7727		216	F7C4	E224	7727	 	 	
+FE837	\uDBBA\uDC37	\xF3\xBE\xA0\xB7	KEYPAD 0		325	F7C9	E5AC	784B	ECC9		134	F990	E6EB	784B		217	F7C5	E225	784B	 	 	
+FE838	\uDBBA\uDC38	\xF3\xBE\xA0\xB8	ANTENNA MARK		381	F357	EA84	7938	ED57	[バリ3]		191	F7AB	E20B	7938	 	 	 
+FE839	\uDBBA\uDC39	\xF3\xBE\xA0\xB9	VIBRATION MODE		393	F363	EA90	7944	ED63	[マナーモード]		260	F7F0	E250	7944	 	 	 
+FE83A	\uDBBA\uDC3A	\xF3\xBE\xA0\xBA	PHONE OFF		394	F364	EA91	7945	ED64	[ケータイOFF]		261	F7F1	E251	7945	 	 	 
+FE83B	\uDBBA\uDC3B	\xF3\xBE\xA0\xBB	KEYPAD 10		189	F747	E52B	7728	EC47	[10]	[10]	 	 	 
+FE83C	\uDBBA\uDC3C	\xF3\xBE\xA0\xBC	PDC		150	F7A3	E586	7825	ECA3	[PDC]	[PDC]	 	 	 
+FE960	\uDBBA\uDD60	\xF3\xBE\xA5\xA0	HAMBURGER		245	F6AF	E4D6	7631	EBAF		54	F8D4	E673	7631		122	F760	E120	7631	 	 	
+FE961	\uDBBA\uDD61	\xF3\xBE\xA5\xA1	RICE BALL		244	F6AE	E4D5	7630	EBAE		362	F9EE	E749	7630		336	F9E2	E342	7630	 	 	
+FE962	\uDBBA\uDD62	\xF3\xBE\xA5\xA2	SHORTCAKE		239	F6A9	E4D0	762B	EBA9		363	F9EF	E74A	762B		70	F987	E046	762B		 	
+FE963	\uDBBA\uDD63	\xF3\xBE\xA5\xA3	STEAMING BOWL		333	F7D1	E5B4	7853	ECD1		365	F9F1	E74C	7853		334	F9E0	E340	7853	 	 	
+FE964	\uDBBA\uDD64	\xF3\xBE\xA5\xA4	BREAD		424	F383	EAAF	7963	ED83		366	F9F2	E74D	7963		327	F9D9	E339	7963	 	 	
+FE965	\uDBBA\uDD65	\xF3\xBE\xA5\xA5	COOKING		240	F6AA	E4D1	762C	EBAA	[フライパン]		161	F788	E147	762C	 	 	 
+FE966	\uDBBA\uDD66	\xF3\xBE\xA5\xA6	SOFT ICE CREAM		425	F384	EAB0	7964	ED84	[ソフトクリーム]		328	F9DA	E33A	7964	 	 	 
+FE967	\uDBBA\uDD67	\xF3\xBE\xA5\xA7	FRENCH FRIES		426	F385	EAB1	7965	ED85	[ポテト]		329	F9DB	E33B	7965	 	 	 
+FE968	\uDBBA\uDD68	\xF3\xBE\xA5\xA8	DUMPLING		427	F386	EAB2	7966	ED86	[だんご]		330	F9DC	E33C	7966	 	 	 
+FE969	\uDBBA\uDD69	\xF3\xBE\xA5\xA9	RICE CRACKER		428	F387	EAB3	7967	ED87	[せんべい]		331	F9DD	E33D	7967	 	 	 
+FE96A	\uDBBA\uDD6A	\xF3\xBE\xA5\xAA	COOKED RICE		429	F388	EAB4	7968	ED88		365	F9F1	E74C	7853		332	F9DE	E33E	7968	 	 	 
+FE96B	\uDBBA\uDD6B	\xF3\xBE\xA5\xAB	SPAGHETTI		430	F389	EAB5	7969	ED89	[パスタ]		333	F9DF	E33F	7969	 	 	 
+FE96C	\uDBBA\uDD6C	\xF3\xBE\xA5\xAC	CURRY AND RICE		431	F38A	EAB6	796A	ED8A	[カレー]		335	F9E1	E341	796A	 	 	 
+FE96D	\uDBBA\uDD6D	\xF3\xBE\xA5\xAD	SEAFOOD CASSEROLE		432	F38B	EAB7	796B	ED8B	[おでん]		337	F9E3	E343	796B	 	 	 
+FE96E	\uDBBA\uDD6E	\xF3\xBE\xA5\xAE	SUSHI		433	F38C	EAB8	796C	ED8C	[すし]		338	F9E4	E344	796C	 	 	 
+FE96F	\uDBBA\uDD6F	\xF3\xBE\xA5\xAF	LUNCHBOX		438	F391	EABD	7971	ED91	[弁当]		346	F9EC	E34C	7971	 	 	 
+FE970	\uDBBA\uDD70	\xF3\xBE\xA5\xB0	POT OF FOOD		439	F392	EABE	7972	ED92	[鍋]		347	F9ED	E34D	7972	 	 	 
+FE971	\uDBBA\uDD71	\xF3\xBE\xA5\xB1	SHAVED ICE		483	F3BE	EAEA	7A40	EDBE	[カキ氷]		410	FB80	E43F	7A40	 	 	 
+FE972	\uDBBA\uDD72	\xF3\xBE\xA5\xB2	MEAT ON A BONE		160	F69D	E4C4	757D	EB9D	[肉]	[肉]	 	 	 
+FE973	\uDBBA\uDD73	\xF3\xBE\xA5\xB3	FISH CAKE WITH SWIRL DESIGN		275	F6C6	E4ED	7648	EBC6		6	F8A4	E643	7522	[なると]	 	 	 
+FE974	\uDBBA\uDD74	\xF3\xBE\xA5\xB4	ROASTED SWEET POTATO		744	F3FB	EB3A	7A7D	EDFB	[やきいも]	[やきいも]	 	 	 
+FE975	\uDBBA\uDD75	\xF3\xBE\xA5\xB5	PIZZA		745	F3FC	EB3B	7A7E	EDFC	[ピザ]	[ピザ]	 	 	 
+FE976	\uDBBA\uDD76	\xF3\xBE\xA5\xB6	TURKEY LEG		746	F440	EB3C	7B21	EE40	[チキン]	[チキン]	 	 	 
+FE977	\uDBBA\uDD77	\xF3\xBE\xA5\xB7	ICE CREAM		760	F44E	EB4A	7B2F	EE4E	[アイスクリーム]	[アイスクリーム]	 	 	 
+FE978	\uDBBA\uDD78	\xF3\xBE\xA5\xB8	DONUT		761	F44F	EB4B	7B30	EE4F	[ドーナツ]	[ドーナツ]	 	 	 
+FE979	\uDBBA\uDD79	\xF3\xBE\xA5\xB9	COOKIE		762	F450	EB4C	7B31	EE50	[クッキー]	[クッキー]	 	 	 
+FE97A	\uDBBA\uDD7A	\xF3\xBE\xA5\xBA	CHOCOLATE BAR		763	F451	EB4D	7B32	EE51	[チョコ]	[チョコ]	 	 	 
+FE97B	\uDBBA\uDD7B	\xF3\xBE\xA5\xBB	CANDY		764	F452	EB4E	7B33	EE52	[キャンディ]	[キャンディ]	 	 	 
+FE97C	\uDBBA\uDD7C	\xF3\xBE\xA5\xBC	LOLLYPOP		765	F453	EB4F	7B34	EE53	[キャンディ]	[キャンディ]	 	 	 
+FE97D	\uDBBA\uDD7D	\xF3\xBE\xA5\xBD	CUSTARD		772	F45A	EB56	7B3B	EE5A	[プリン]	[プリン]	 	 	 
+FE97E	\uDBBA\uDD7E	\xF3\xBE\xA5\xBE	HONEY POT		775	F45D	EB59	7B3E	EE5D	[ハチミツ]	[ハチミツ]	 	 	 
+FE97F	\uDBBA\uDD7F	\xF3\xBE\xA5\xBF	FRIED SHRIMP		798	F474	EB70	7B55	EE74	[エビフライ]	[エビフライ]	 	 	 
+FE980	\uDBBA\uDD80	\xF3\xBE\xA6\x80	FORK AND KNIFE		146	F685	E4AC	7565	EB85		50	F8D0	E66F	7565		67	F984	E043	7565		 	
+FE981	\uDBBA\uDD81	\xF3\xBE\xA6\x81	HOT BEVERAGE		93	F7B4	E597	7836	ECB4		51	F8D1	E670	7836		69	F986	E045	7836			
+FE982	\uDBBA\uDD82	\xF3\xBE\xA6\x82	COCKTAIL GLASS		52	F69B	E4C2	757B	EB9B		52	F8D2	E671	757B		68	F985	E044	757B			
+FE983	\uDBBA\uDD83	\xF3\xBE\xA6\x83	BEER		65	F69C	E4C3	757C	EB9C		53	F8D3	E672	757C		71	F988	E047	757C			
+FE984	\uDBBA\uDD84	\xF3\xBE\xA6\x84	TEA CUP		423	F382	EAAE	7962	ED82		319	F9C3	E71E	7962		326	F9D8	E338	7962	 	 	
+FE985	\uDBBA\uDD85	\xF3\xBE\xA6\x85	SAKE BOTTLE AND CUP		400	F36A	EA97	794B	ED6A		364	F9F0	E74B	794B		281	F9AB	E30B	794B	 	 	
+FE986	\uDBBA\uDD86	\xF3\xBE\xA6\x86	WINE GLASS		12	F69A	E4C1	757A	EB9A		375	F9FB	E756	757A		68	F985	E044	757B	 	 	
+FE987	\uDBBA\uDD87	\xF3\xBE\xA6\x87	CLINKING BEER MUGS		401	F36B	EA98	794C	ED6B		53	F8D3	E672	757C		282	F9AC	E30C	794C	 	 	 
+FE988	\uDBBA\uDD88	\xF3\xBE\xA6\x88	TROPICAL DRINK		748	F442	EB3E	7B23	EE42		52	F8D2	E671	757B		68	F985	E044	757B	 	 	 
+FEAF0	\uDBBA\uDEF0	\xF3\xBE\xAB\xB0	NORTH EAST ARROW		70	F771	E555	7752	EC71		59	F8D9	E678	7752		234	F7D6	E236	7752	 	 	
+FEAF1	\uDBBA\uDEF1	\xF3\xBE\xAB\xB1	SOUTH EAST ARROW		43	F769	E54D	774A	EC69		89	F8F7	E696	774A		236	F7D8	E238	774A	 	 	
+FEAF2	\uDBBA\uDEF2	\xF3\xBE\xAB\xB2	NORTH WEST ARROW		42	F768	E54C	7749	EC68		90	F8F8	E697	7749		235	F7D7	E237	7749	 	 	
+FEAF3	\uDBBA\uDEF3	\xF3\xBE\xAB\xB3	SOUTH WEST ARROW		71	F772	E556	7753	EC72		104	F949	E6A5	7753		237	F7D9	E239	7753	 	 	
+FEAF4	\uDBBA\uDEF4	\xF3\xBE\xAB\xB4	RIGHTWARDS ARROW CURVED UPWARDS		731	F3EE	EB2D	7A70	EDEE		145	F99A	E6F5	7A70		234	F7D6	E236	7752	 	 	
+FEAF5	\uDBBA\uDEF5	\xF3\xBE\xAB\xB5	RIGHTWARDS ARROW CURVED DOWNWARDS		732	F3EF	EB2E	7A71	EDEF		156	F9A5	E700	7A71		236	F7D8	E238	774A	 	 	
+FEAF6	\uDBBA\uDEF6	\xF3\xBE\xAB\xB6	LEFT RIGHT ARROW		808	F47E	EB7A	7B5F	EE7E		349	F9E1	E73C	7B5F	⇔	 	 	
+FEAF7	\uDBBA\uDEF7	\xF3\xBE\xAB\xB7	UP DOWN ARROW		809	F480	EB7B	7B60	EE80		350	F9E2	E73D	7B60	↑↓	 	 	
+FEAF8	\uDBBA\uDEF8	\xF3\xBE\xAB\xB8	UPWARDS ARROW		29	F75B	E53F	773C	EC5B	[↑]		230	F7D2	E232	773C	 	 	 
+FEAF9	\uDBBA\uDEF9	\xF3\xBE\xAB\xB9	DOWNWARDS ARROW		30	F75C	E540	773D	EC5C	[↓]		231	F7D3	E233	773D	 	 	 
+FEAFA	\uDBBA\uDEFA	\xF3\xBE\xAB\xBA	RIGHTWARDS ARROW		63	F76E	E552	774F	EC6E	[→]		232	F7D4	E234	774F	 	 	 
+FEAFB	\uDBBA\uDEFB	\xF3\xBE\xAB\xBB	LEFTWARDS ARROWS		64	F76F	E553	7750	EC6F	[←]		233	F7D5	E235	7750	 	 	 
+FEAFC	\uDBBA\uDEFC	\xF3\xBE\xAB\xBC	BLACK RIGHT-POINTING TRIANGLE		6	F74A	E52E	772B	EC4A	[>]		238	F7DA	E23A	772B	 	 	 
+FEAFD	\uDBBA\uDEFD	\xF3\xBE\xAB\xBD	BLACK LEFT-POINTING TRIANGLE		5	F749	E52D	772A	EC49	[<]		239	F7DB	E23B	772A	 	 	 
+FEAFE	\uDBBA\uDEFE	\xF3\xBE\xAB\xBE	BLACK RIGHT-POINTING DOUBLE TRIANGLE		8	F74C	E530	772D	EC4C	[>>]		240	F7DC	E23C	772D	 	 	 
+FEAFF	\uDBBA\uDEFF	\xF3\xBE\xAB\xBF	BLACK LEFT-POINTING DOUBLE TRIANGLE		7	F74B	E52F	772C	EC4B	[<<]		241	F7DD	E23D	772C	 	 	 
+FEB00	\uDBBA\uDF00	\xF3\xBE\xAC\x80	BLACK DOWN-POINTING SMALL TRIANGLE		32	F75E	E542	773F	EC5E	▼	▼	 	 	 
+FEB01	\uDBBA\uDF01	\xF3\xBE\xAC\x81	BLACK UP-POINTING SMALL TRIANGLE		33	F75F	E543	7740	EC5F	▲	▲	 	 	 
+FEB02	\uDBBA\uDF02	\xF3\xBE\xAC\x82	BLACK DOWN-POINTING DOUBLE TRIANGLES		34	F760	E544	7741	EC60	▼	▼	 	 	 
+FEB03	\uDBBA\uDF03	\xF3\xBE\xAC\x83	BLACK UP-POINTING DOUBLE TRIANGLES		35	F761	E545	7742	EC61	▲	▲	 	 	 
+FEB04	\uDBBA\uDF04	\xF3\xBE\xAC\x84	HEAVY EXCLAMATION MARK ORNAMENT		2	F65A	E482	753B	EB5A		158	F9A7	E702	753B		33	F961	E021	753B		 	
+FEB05	\uDBBA\uDF05	\xF3\xBE\xAC\x85	EXCLAMATION QUESTION MARK		733	F3F0	EB2F	7A72	EDF0		159	F9A8	E703	7A72	!?		 	
+FEB06	\uDBBA\uDF06	\xF3\xBE\xAC\x86	DOUBLE EXCLAMATION MARK		734	F3F1	EB30	7A73	EDF1		160	F9A9	E704	7A73	!!		 	
+FEB07	\uDBBA\uDF07	\xF3\xBE\xAC\x87	WAVY LENGTH MARK	〓		165	F9AE	E709	7E2A	〓	 	 	
+FEB08	\uDBBA\uDF08	\xF3\xBE\xAC\x88	LOOPED LENGTH MARK		735	F3F2	EB31	7A74	EDF2		166	F9AF	E70A	7A74	~	 	 	
+FEB09	\uDBBA\uDF09	\xF3\xBE\xAC\x89	QUESTION MARK		3	F65B	E483	753C	EB5B	[?]		32	F960	E020	753C		 	 
+FEB0A	\uDBBA\uDF0A	\xF3\xBE\xAC\x8A	QUESTION MARK 2		3	F65B	E483	753C	EB5B	[?]		324	F9D6	E336	7D22	 	 	 
+FEB0B	\uDBBA\uDF0B	\xF3\xBE\xAC\x8B	EXCLAMATION MARK		2	F65A	E482	753B	EB5A		158	F9A7	E702	753B		325	F9D7	E337	7D21	 	 	 
+FEB0C	\uDBBA\uDF0C	\xF3\xBE\xAC\x8C	HEAVY BLACK HEART		51	F7B2	E595	7834	ECB2		136	F991	E6EC	7834		34	F962	E022	7834			
+FEB0D	\uDBBA\uDF0D	\xF3\xBE\xAC\x8D	HEART 2		803	F479	EB75	7B5A	EE79		137	F992	E6ED	7B5A		309	F9C7	E327	795A	 	 	
+FEB0E	\uDBBA\uDF0E	\xF3\xBE\xAC\x8E	HEART 3		265	F64F	E477	7530	EB4F		138	F993	E6EE	7530		35	F963	E023	7530			
+FEB0F	\uDBBA\uDF0F	\xF3\xBE\xAC\x8F	HEART 4		266	F650	E478	7531	EB50		139	F994	E6EF	7531		309	F9C7	E327	795A	 	 	
+FEB10	\uDBBA\uDF10	\xF3\xBE\xAC\x90	HEART 5		415	F379	EAA6	795A	ED79		136	F991	E6EC	7834		309	F9C7	E327	795A	 	 	 
+FEB11	\uDBBA\uDF11	\xF3\xBE\xAC\x91	HEART 6		803	F479	EB75	7B5A	EE79		137	F992	E6ED	7B5A		310	F9C8	E328	7B5A	 	 	 
+FEB12	\uDBBA\uDF12	\xF3\xBE\xAC\x92	HEART 7		272	F6C3	E4EA	7645	EBC3		136	F991	E6EC	7834		311	F9C9	E329	7645	 	 	 
+FEB13	\uDBBA\uDF13	\xF3\xBE\xAC\x93	HEART WITH BLUE COLOR		416	F37A	EAA7	795B	ED7A		136	F991	E6EC	7834		312	F9CA	E32A	795B	 	 	 
+FEB14	\uDBBA\uDF14	\xF3\xBE\xAC\x94	HEART WITH GREEN COLOR		417	F37B	EAA8	795C	ED7B		136	F991	E6EC	7834		313	F9CB	E32B	795C	 	 	 
+FEB15	\uDBBA\uDF15	\xF3\xBE\xAC\x95	HEART WITH YELLOW COLOR		418	F37C	EAA9	795D	ED7C		136	F991	E6EC	7834		314	F9CC	E32C	795D	 	 	 
+FEB16	\uDBBA\uDF16	\xF3\xBE\xAC\x96	HEART WITH PURPLE COLOR		419	F37D	EAAA	795E	ED7D		136	F991	E6EC	7834		315	F9CD	E32D	795E	 	 	 
+FEB17	\uDBBA\uDF17	\xF3\xBE\xAC\x97	HEART WITH RIBBON		770	F458	EB54	7B39	EE58		136	F991	E6EC	7834		402	FB77	E437	7B39	 	 	 
+FEB18	\uDBBA\uDF18	\xF3\xBE\xAC\x98	REVOLVING HEARTS		328	F7CC	E5AF	784E	ECCC		137	F992	E6ED	7B5A		309	F9C7	E327	795A	 	 	 
+FEB19	\uDBBA\uDF19	\xF3\xBE\xAC\x99	DECORATIVE DESIGN 1		51	F7B2	E595	7834	ECB2		148	F99D	E6F8	7E29		184	F7A4	E204	7D33	 	 	 
+FEB1A	\uDBBA\uDF1A	\xF3\xBE\xAC\x9A	BLACK HEART SUIT		414	F378	EAA5	7959	ED78		80	F8EE	E68D	7959		192	F7AC	E20C	7959	 	 	
+FEB1B	\uDBBA\uDF1B	\xF3\xBE\xAC\x9B	BLACK SPADE SUIT		314	F7BE	E5A1	7840	ECBE		81	F8EF	E68E	7840		194	F7AE	E20E	7840	 	 	
+FEB1C	\uDBBA\uDF1C	\xF3\xBE\xAC\x9C	BLACK DIAMOND SUIT		315	F7BF	E5A2	7841	ECBF		82	F8F0	E68F	7841		193	F7AD	E20D	7841	 	 	
+FEB1D	\uDBBA\uDF1D	\xF3\xBE\xAC\x9D	BLACK CLUB SUIT		316	F7C0	E5A3	7842	ECC0		83	F8F1	E690	7842		195	F7AF	E20F	7842	 	 	
+FEB1E	\uDBBA\uDF1E	\xF3\xBE\xAC\x9E	SMOKING SIGN		176	F655	E47D	7536	EB55		66	F8E0	E67F	7536		284	F9AE	E30E	7536	 	 	
+FEB1F	\uDBBA\uDF1F	\xF3\xBE\xAC\x9F	NO SMOKING SIGN		177	F656	E47E	7537	EB56		67	F8E1	E680	7537		188	F7A8	E208	7537	 	 	
+FEB20	\uDBBA\uDF20	\xF3\xBE\xAC\xA0	WHEELCHAIR SYMBOL		178	F657	E47F	7538	EB57		94	F8FC	E69B	7538		190	F7AA	E20A	7538	 	 	
+FEB21	\uDBBA\uDF21	\xF3\xBE\xAC\xA1	FREE SIGN		299	F795	E578	7775	EC95		114	F97B	E6D7	7775	[FREE]	 	 	
+FEB22	\uDBBA\uDF22	\xF3\xBE\xAC\xA2	TRIANGULAR FLAG ON POST		730	F3ED	EB2C	7A6F	EDED		121	F983	E6DE	7A6F	[旗]	 	 	
+FEB23	\uDBBA\uDF23	\xF3\xBE\xAC\xA3	WARNING SIGN		1	F659	E481	753A	EB59		344	F9DC	E737	753A		262	F7F2	E252	753A	 	 	
+FEB24	\uDBBA\uDF24	\xF3\xBE\xAC\xA4	HERE SIGN	[ココ]	[ココ]		183	F7A3	E203	7D5B	 	 	 
+FEB25	\uDBBA\uDF25	\xF3\xBE\xAC\xA5	ADULTS ONLY SIGN		380	F356	EA83	7937	ED56	[18禁]		187	F7A7	E207	7937	 	 	
+FEB26	\uDBBA\uDF26	\xF3\xBE\xAC\xA6	NO ENTRANCE SIGN		98	F65C	E484	753D	EB5C		336	F9D4	E72F	7E36		145	F777	E137	792B	 		 
+FEB27	\uDBBA\uDF27	\xF3\xBE\xAC\xA7	OK SIGN		326	F7CA	E5AD	784C	ECCA		135	F9B0	E70B	784C		257	F7ED	E24D	784C	 	 	
+FEB28	\uDBBA\uDF28	\xF3\xBE\xAC\xA8	NO GOOD SIGN	[NG]		336	F9D4	E72F	7E36	[NG]	 	 	
+FEB29	\uDBBA\uDF29	\xF3\xBE\xAC\xA9	COPYRIGHT SIGN		81	F774	E558	7755	EC74		338	F9D6	E731	7755		258	F7EE	E24E	7755	 	 	
+FEB2A	\uDBBA\uDF2A	\xF3\xBE\xAC\xAA	TRADE MARK SIGN		54	F76A	E54E	774B	EC6A		339	F9D7	E732	774B		478	FBD7	E537	774B	 	 	
+FEB2B	\uDBBA\uDF2B	\xF3\xBE\xAC\xAB	TOP SECRET SIGN		279	F6CA	E4F1	764C	EBCA		341	F9D9	E734	764C		291	F9B5	E315	764C		 	
+FEB2C	\uDBBA\uDF2C	\xF3\xBE\xAC\xAC	BLACK UNIVERSAL RECYCLING SYMBOL		807	F47D	EB79	7B5E	EE7D		342	F9DA	E735	7B5E	↑↓	 	 	
+FEB2D	\uDBBA\uDF2D	\xF3\xBE\xAC\xAD	REGISTERED SIGN		82	F775	E559	7756	EC75		343	F9DB	E736	7756		259	F7EF	E24F	7756	 	 	
+FEB2E	\uDBBA\uDF2E	\xF3\xBE\xAC\xAE	PROHIBITED SIGN	[禁]		345	F9DD	E738	7E37	[禁]	 	 	
+FEB2F	\uDBBA\uDF2F	\xF3\xBE\xAC\xAF	EMPTY SIGN		387	F35D	EA8A	793E	ED5D		346	F9DE	E739	793E		223	F7CB	E22B	793E	 	 	
+FEB30	\uDBBA\uDF30	\xF3\xBE\xAC\xB0	PASSED SIGN	[合]		347	F9DF	E73A	7E38	[合]	 	 	
+FEB31	\uDBBA\uDF31	\xF3\xBE\xAC\xB1	FULL SIGN		386	F35C	EA89	793D	ED5C		348	F9E0	E73B	793D		222	F7CA	E22A	793D	 	 	
+FEB32	\uDBBA\uDF32	\xF3\xBE\xAC\xB2	VS SIGN		363	F345	E5D2	7926	ED45	[VS]		136	F76E	E12E	7926	 	 	 
+FEB33	\uDBBA\uDF33	\xF3\xBE\xAC\xB3	MENS SYMBOL	[♂]	[♂]		146	F778	E138	7D58	 	 	 
+FEB34	\uDBBA\uDF34	\xF3\xBE\xAC\xB4	WOMENS SYMBOL	[♀]	[♀]		147	F779	E139	7D59	 	 	 
+FEB35	\uDBBA\uDF35	\xF3\xBE\xAC\xB5	BABY 2		710	F3D9	EB18	7A5B	EDD9	[赤ちゃん]		148	F77A	E13A	7D54	 	 	 
+FEB36	\uDBBA\uDF36	\xF3\xBE\xAC\xB6	NEW SIGN		334	F7E5	E5B5	7867	ECE5		120	F982	E6DD	7867		198	F7B2	E212	7867	 	 	
+FEB37	\uDBBA\uDF37	\xF3\xBE\xAC\xB7	UP SIGN		303	F6E8	E50F	766A	EBE8	[UP!]		199	F7B3	E213	766A	 	 	 
+FEB38	\uDBBA\uDF38	\xF3\xBE\xAC\xB8	COOL SIGN		382	F358	EA85	7939	ED58	[COOL]		200	F7B4	E214	7939	 	 	 
+FEB39	\uDBBA\uDF39	\xF3\xBE\xAC\xB9	EXISTENCE SIGN	[有]	[有]		201	F7B5	E215	7D5D	 	 	 
+FEB3A	\uDBBA\uDF3A	\xF3\xBE\xAC\xBA	NON-EXISTENCE SIGN	[無]	[無]		202	F7B6	E216	7D5E	 	 	 
+FEB3B	\uDBBA\uDF3B	\xF3\xBE\xAC\xBB	MONTHLY SIGN	[月]	[月]		203	F7B7	E217	7D5F	 	 	 
+FEB3C	\uDBBA\uDF3C	\xF3\xBE\xAC\xBC	APPLICATION SIGN	[申]	[申]		204	F7B8	E218	7D60	 	 	 
+FEB3D	\uDBBA\uDF3D	\xF3\xBE\xAC\xBD	ADVANTAGE SIGN		285	F6D0	E4F7	7652	EBD0	[得]		218	F7C6	E226	7652	 	 	 
+FEB3E	\uDBBA\uDF3E	\xF3\xBE\xAC\xBE	DISCOUNT SIGN		383	F359	EA86	793A	ED59	[割]		219	F7C7	E227	793A	 	 	 
+FEB3F	\uDBBA\uDF3F	\xF3\xBE\xAC\xBF	SERVICE SIGN		384	F35A	EA87	793B	ED5A	[サービス]		220	F7C8	E228	793B	 	 	 
+FEB40	\uDBBA\uDF40	\xF3\xBE\xAD\x80	RESERVED SIGN		388	F35E	EA8B	793F	ED5E	[指]		224	F7CC	E22C	793F	 	 	 
+FEB41	\uDBBA\uDF41	\xF3\xBE\xAD\x81	IN BUSINESS SIGN		389	F35F	EA8C	7940	ED5F	[営]		225	F7CD	E22D	7940	 	 	 
+FEB42	\uDBBA\uDF42	\xF3\xBE\xAD\x82	TOP WITH UPWARD SIGN	[TOP]	[TOP]		256	F7EC	E24C	7D61	 	 	 
+FEB43	\uDBBA\uDF43	\xF3\xBE\xAD\x83	CELEBRATION SIGN		402	F36C	EA99	794D	ED6C	[祝]		283	F9AD	E30D	794D	 	 	 
+FEB44	\uDBBA\uDF44	\xF3\xBE\xAD\x84	BLACK CIRCLE MARK		422	F381	EAAD	7961	ED81		99	F944	E6A0	7E24		320	F9D2	E332	7961	 	 	 
+FEB45	\uDBBA\uDF45	\xF3\xBE\xAD\x85	CROSS MARK		61	F76C	E550	774D	EC6C	[×]		321	F9D3	E333	774D	 	 	 
+FEB46	\uDBBA\uDF46	\xF3\xBE\xAD\x86	CROSS MARK 2		62	F76D	E551	774E	EC6D	[×]		321	F9D3	E333	774D	 	 	 
+FEB47	\uDBBA\uDF47	\xF3\xBE\xAD\x87	INFORMATION SYMBOL		11	F74F	E533	7730	EC4F	[i]	[i]	 	 	 
+FEB48	\uDBBA\uDF48	\xF3\xBE\xAD\x88	NO ENTRY SIGN		31	F75D	E541	773E	EC5D		345	F9DD	E738	7E37	[禁止]	 	 	 
+FEB49	\uDBBA\uDF49	\xF3\xBE\xAD\x89	HEAVY CHECK MARK		73	F773	E557	7754	EC73	[チェックマーク]	[チェックマーク]	 	 	 
+FEB4A	\uDBBA\uDF4A	\xF3\xBE\xAD\x8A	HEAVY CHECK MARK 2		132	F77A	E55E	775B	EC7A	[チェックマーク]	[チェックマーク]	 	 	 
+FEB4B	\uDBBA\uDF4B	\xF3\xBE\xAD\x8B	LINK SYMBOL		164	F7A7	E58A	7829	ECA7	[リンク]	[リンク]	 	 	 
+FEB4C	\uDBBA\uDF4C	\xF3\xBE\xAD\x8C	FULL BLANK SPACE		173	F7A9	E58C	782B	ECA9	〓	〓	 	 	 
+FEB4D	\uDBBA\uDF4D	\xF3\xBE\xAD\x8D	A HALF BLANK SPACE		174	F7AA	E58D	782C	ECAA	〓	〓	 	 	 
+FEB4E	\uDBBA\uDF4E	\xF3\xBE\xAD\x8E	A QUARTER BLANK SPACE		175	F7AB	E58E	782D	ECAB	〓	〓	 	 	 
+FEB4F	\uDBBA\uDF4F	\xF3\xBE\xAD\x8F	SOS SIGN		270	F6C1	E4E8	7643	EBC1	[SOS]	[SOS]	 	 	 
+FEB50	\uDBBA\uDF50	\xF3\xBE\xAD\x90	ACCEPT SIGN		506	F7D8	EB01	785A	ECD8	[可]	[可]	 	 	 
+FEB51	\uDBBA\uDF51	\xF3\xBE\xAD\x91	HEAVY PLUS		26	F758	E53C	7739	EC58	[+]	[+]	 	 	 
+FEB52	\uDBBA\uDF52	\xF3\xBE\xAD\x92	HEAVY MINUS		27	F759	E53D	773A	EC59	[-]	[-]	 	 	 
+FEB53	\uDBBA\uDF53	\xF3\xBE\xAD\x93	HEAVY TIMES		55	F76B	E54F	774C	EC6B	[×]		321	F9D3	E333	774D	 	 	 
+FEB54	\uDBBA\uDF54	\xF3\xBE\xAD\x94	HEAVY DIVISION		66	F770	E554	7751	EC70	[÷]	[÷]	 	 	 
+FEB55	\uDBBA\uDF55	\xF3\xBE\xAD\x95	DIAMOND SHAPE WITH A DOT INSIDE	〓		148	F99D	E6F8	7E29	〓	 	 	
+FEB56	\uDBBA\uDF56	\xF3\xBE\xAD\x96	ELECTRIC LIGHT BULB		77	F64E	E476	752F	EB4E		151	F9A0	E6FB	752F		105	F74F	E10F	752F		 	
+FEB57	\uDBBA\uDF57	\xF3\xBE\xAD\x97	ANGER SIGN		262	F6BE	E4E5	7640	EBBE		152	F9A1	E6FC	7640		322	F9D4	E334	7640	 	 	
+FEB58	\uDBBA\uDF58	\xF3\xBE\xAD\x98	BOMB		268	F652	E47A	7533	EB52		154	F9A3	E6FE	7533		287	F9B1	E311	7533	 	 	
+FEB59	\uDBBA\uDF59	\xF3\xBE\xAD\x99	SLEEPING SIGN		261	F64D	E475	752E	EB4D		157	F9A6	E701	752E		150	F77C	E13C	752E	 	 	
+FEB5A	\uDBBA\uDF5A	\xF3\xBE\xAD\x9A	COLLISION SYMBOL		329	F7CD	E5B0	784F	ECCD		161	F9AA	E705	784F	[ドンッ]	 	 	
+FEB5B	\uDBBA\uDF5B	\xF3\xBE\xAD\x9B	SPLASHING SWEAT		330	F7CE	E5B1	7850	ECCE		162	F9AB	E706	7850		319	F9D1	E331	7850	 	 	
+FEB5C	\uDBBA\uDF5C	\xF3\xBE\xAD\x9C	DRIP		263	F6BF	E4E6	7641	EBBF		163	F9AC	E707	7641		319	F9D1	E331	7850	 		
+FEB5D	\uDBBA\uDF5D	\xF3\xBE\xAD\x9D	DASH SYMBOL		282	F6CD	E4F4	764F	EBCD		164	F9AD	E708	764F		318	F9D0	E330	764F	 	 	
+FEB5E	\uDBBA\uDF5E	\xF3\xBE\xAD\x9E	FLEXED BICEP		271	F6C2	E4E9	7644	EBC2	[力こぶ]		166	F78D	E14C	7644	 	 	 
+FEB5F	\uDBBA\uDF5F	\xF3\xBE\xAD\x9F	DIZZY SYMBOL		778	F460	EB5C	7B41	EE60	[クラクラ]		354	FB47	E407	7D4F	 	 	 
+FEB60	\uDBBA\uDF60	\xF3\xBE\xAD\xA0	SPARKLES		420	F37E	EAAB	795F	ED7E		150	F99F	E6FA	795F		316	F9CE	E32E	795F		 	
+FEB61	\uDBBA\uDF61	\xF3\xBE\xAD\xA1	EIGHT POINTED BLACK STAR		267	F651	E479	7532	EB51		148	F99D	E6F8	7E29		185	F7A5	E205	7532	 	 	 
+FEB62	\uDBBA\uDF62	\xF3\xBE\xAD\xA2	EIGHT SPOKED ASTERISK		28	F75A	E53E	773B	EC5A		148	F99D	E6F8	7E29		186	F7A6	E206	773B	 	 	 
+FEB63	\uDBBA\uDF63	\xF3\xBE\xAD\xA3	BLACK CIRCLE		40	F766	E54A	7747	EC66		95	F940	E69C	7847		205	F7B9	E219	7747	 	 	 
+FEB64	\uDBBA\uDF64	\xF3\xBE\xAD\xA4	BLACK CIRCLE 2		41	F767	E54B	7748	EC67		95	F940	E69C	7847		206	F7BA	E21A	7748	 	 	 
+FEB65	\uDBBA\uDF65	\xF3\xBE\xAD\xA5	MEDIUM BLACK CIRCLE		23	F756	E53A	7737	EC56		95	F940	E69C	7847		205	F7B9	E219	7747	 	 	 
+FEB66	\uDBBA\uDF66	\xF3\xBE\xAD\xA6	MEDIUM BLACK CIRCLE 2		24	F757	E53B	7738	EC57		95	F940	E69C	7847		205	F7B9	E219	7747	 	 	 
+FEB67	\uDBBA\uDF67	\xF3\xBE\xAD\xA7	OCTAGON		41	F767	E54B	7748	EC67		95	F940	E69C	7847		207	F7BB	E21B	7D25	 	 	 
+FEB68	\uDBBA\uDF68	\xF3\xBE\xAD\xA8	BLACK STAR		69	F663	E48B	7544	EB63	[☆]		317	F9CF	E32F	7544		 	 
+FEB69	\uDBBA\uDF69	\xF3\xBE\xAD\xA9	BLACK STAR 2		69	F663	E48B	7544	EB63	[☆]		323	F9D5	E335	7D34	 	 	 
+FEB6A	\uDBBA\uDF6A	\xF3\xBE\xAD\xAA	BLACK STAR 3		75	F640	E468	7521	EB40	☆彡	☆彡	 	 	 
+FEB6B	\uDBBA\uDF6B	\xF3\xBE\xAD\xAB	BLACK SQUARE		38	F764	E548	7745	EC64	■		207	F7BB	E21B	7D25	 	 	 
+FEB6C	\uDBBA\uDF6C	\xF3\xBE\xAD\xAC	BLACK SQUARE 2		39	F765	E549	7746	EC65	■		206	F7BA	E21A	7748	 	 	 
+FEB6D	\uDBBA\uDF6D	\xF3\xBE\xAD\xAD	BLACK SMALL SQUARE		9	F74D	E531	772E	EC4D	■		207	F7BB	E21B	7D25	 	 	 
+FEB6E	\uDBBA\uDF6E	\xF3\xBE\xAD\xAE	BLACK SMALL SQUARE 2		10	F74E	E532	772F	EC4E	■		206	F7BA	E21A	7748	 	 	 
+FEB6F	\uDBBA\uDF6F	\xF3\xBE\xAD\xAF	BLACK MEDIUM SMALL SQUARE		17	F750	E534	7731	EC50	■		207	F7BB	E21B	7D25	 	 	 
+FEB70	\uDBBA\uDF70	\xF3\xBE\xAD\xB0	BLACK MEDIUM SMALL SQUARE 2		18	F751	E535	7732	EC51	■		206	F7BA	E21A	7748	 	 	 
+FEB71	\uDBBA\uDF71	\xF3\xBE\xAD\xB1	BLACK MEDIUM SQUARE		21	F754	E538	7735	EC54	■		207	F7BB	E21B	7D25	 	 	 
+FEB72	\uDBBA\uDF72	\xF3\xBE\xAD\xB2	BLACK MEDIUM SQUARE 2		22	F755	E539	7736	EC55	■		206	F7BA	E21A	7748	 	 	 
+FEB73	\uDBBA\uDF73	\xF3\xBE\xAD\xB3	BLACK DIAMOND		36	F762	E546	7743	EC62	◆		207	F7BB	E21B	7D25	 	 	 
+FEB74	\uDBBA\uDF74	\xF3\xBE\xAD\xB4	BLACK DIAMOND 2		37	F763	E547	7744	EC63	◆		207	F7BB	E21B	7D25	 	 	 
+FEB75	\uDBBA\uDF75	\xF3\xBE\xAD\xB5	BLACK SMALL DIAMOND		19	F752	E536	7733	EC52	◆		207	F7BB	E21B	7D25	 	 	 
+FEB76	\uDBBA\uDF76	\xF3\xBE\xAD\xB6	BLACK SMALL DIAMOND 2		20	F753	E537	7734	EC53	◆		207	F7BB	E21B	7D25	 	 	 
+FEB77	\uDBBA\uDF77	\xF3\xBE\xAD\xB7	SPARKLE		76	F644	E46C	7525	EB44		150	F99F	E6FA	795F		316	F9CE	E32E	795F	 	 	 
+FEB78	\uDBBA\uDF78	\xF3\xBE\xAD\xB8	BLACK UP-POINTING TRIANGLE		88	F776	E55A	7757	EC76	▲	▲	 	 	 
+FEB79	\uDBBA\uDF79	\xF3\xBE\xAD\xB9	BLACK DOWN-POINTING TRIANGLE		89	F777	E55B	7758	EC77	▼	▼	 	 	 
+FEB7A	\uDBBA\uDF7A	\xF3\xBE\xAD\xBA	WHITE FLOWER		278	F6C9	E4F0	764B	EBC9	[花丸]	[花丸]	 	 	 
+FEB7B	\uDBBA\uDF7B	\xF3\xBE\xAD\xBB	HUNDRED POINTS SYMBOL		280	F6CB	E4F2	764D	EBCB	[100点]	[100点]	 	 	 
+FEB7C	\uDBBA\uDF7C	\xF3\xBE\xAD\xBC	LATIN CAPITAL LETTERS SYMBOL		502	F7D4	EAFD	7856	ECD4	[ABCD]	[ABCD]	 	 	 
+FEB7D	\uDBBA\uDF7D	\xF3\xBE\xAD\xBD	LATIN SMALL LETTERS SYMBOL		503	F7D5	EAFE	7857	ECD5	[abcd]	[abcd]	 	 	 
+FEB7E	\uDBBA\uDF7E	\xF3\xBE\xAD\xBE	NUMBERS SYMBOL		504	F7D6	EAFF	7858	ECD6	[1234]	[1234]	 	 	 
+FEB7F	\uDBBA\uDF7F	\xF3\xBE\xAD\xBF	SYMBOLS SIGN		505	F7D7	EB00	7859	ECD7	[記号]	[記号]	 	 	 
+FEB80	\uDBBA\uDF80	\xF3\xBE\xAE\x80	ENGLISH LANGUAGE SYMBOL		771	F459	EB55	7B3A	EE59	[ABC]	[ABC]	 	 	 
+FEB81	\uDBBA\uDF81	\xF3\xBE\xAE\x81	ID		385	F35B	EA88	793C	ED5B		115	F97C	E6D8	793C		221	F7C9	E229	793C	 	 	
+FEB82	\uDBBA\uDF82	\xF3\xBE\xAE\x82	KEY		120	F6F2	E519	7674	EBF2		116	F97D	E6D9	7674		63	F980	E03F	7674	 	 	
+FEB83	\uDBBA\uDF83	\xF3\xBE\xAE\x83	LEFTWARDS ARROW WITH HOOK		118	F779	E55D	775A	EC79		117	F97E	E6DA	775A	←┘	 	 	
+FEB84	\uDBBA\uDF84	\xF3\xBE\xAE\x84	CLEAR SIGN		324	F7C8	E5AB	784A	ECC8		118	F980	E6DB	784A	[CL]	 	 	
+FEB85	\uDBBA\uDF85	\xF3\xBE\xAE\x85	MAGNIFYING GLASS		119	F6F1	E518	7673	EBF1		119	F981	E6DC	7673		110	F754	E114	7673	 	 	
+FEB86	\uDBBA\uDF86	\xF3\xBE\xAE\x86	LOCK		138	F6F5	E51C	7677	EBF5		116	F97D	E6D9	7674		158	F785	E144	7677	 	 	 
+FEB87	\uDBBA\uDF87	\xF3\xBE\xAE\x87	LOCK 2		138	F6F5	E51C	7677	EBF5		116	F97D	E6D9	7674		159	F786	E145	7D3E	 	 	 
+FEB88	\uDBBA\uDF88	\xF3\xBE\xAE\x88	RIGHTWARDS ARROW WITH HOOK		117	F778	E55C	7759	EC78	└→	└→	 	 	 
+FEB89	\uDBBA\uDF89	\xF3\xBE\xAE\x89	OPENWAVE		500	F7D2	EAFB	7854	ECD2	[オープンウェブ]	[オープンウェブ]	 	 	 
+FEB8A	\uDBBA\uDF8A	\xF3\xBE\xAE\x8A	CLOSED LOCK WITH KEY		501	F7D3	EAFC	7855	ECD3		116	F97D	E6D9	7674		158	F785	E144	7677	 	 	 
+FEB8B	\uDBBA\uDF8B	\xF3\xBE\xAE\x8B	BALLOT BOX WITH CHECK		507	F7D9	EB02	785B	ECD9	[チェックマーク]	[チェックマーク]	 	 	 
+FEB8C	\uDBBA\uDF8C	\xF3\xBE\xAE\x8C	RADIO BUTTON		509	F7DB	EB04	785D	ECDB	[ラジオボタン]	[ラジオボタン]	 	 	 
+FEB8D	\uDBBA\uDF8D	\xF3\xBE\xAE\x8D	MAGNIFYING GLASS 2		510	F7DC	EB05	785E	ECDC		119	F981	E6DC	7673		110	F754	E114	7673	 	 	 
+FEB8E	\uDBBA\uDF8E	\xF3\xBE\xAE\x8E	LEFTWARDS ARROW WITH LETTER BACK		511	F7DD	EB06	785F	ECDD	[←BACK]		233	F7D5	E235	7750	 	 	 
+FEB8F	\uDBBA\uDF8F	\xF3\xBE\xAE\x8F	BOOKMARK		512	F7DE	EB07	7860	ECDE	[ブックマーク]	[ブックマーク]	 	 	 
+FEB90	\uDBBA\uDF90	\xF3\xBE\xAE\x90	LOCK WITH INK PEN		517	F7E3	EB0C	7865	ECE3		116	F97D	E6D9	7674		158	F785	E144	7677	 	 	 
+FEB91	\uDBBA\uDF91	\xF3\xBE\xAE\x91	DOUBLE CLOCKWISE OPEN CIRCLE ARROWS		518	F7E4	EB0D	7866	ECE4		342	F9DA	E735	7B5E	↑↓	 	 	 
+FEB92	\uDBBA\uDF92	\xF3\xBE\xAE\x92	EMAIL SIGN		799	F475	EB71	7B56	EE75		110	F977	E6D3	767C		93	F743	E103	7B47	 	 	 
+FEB93	\uDBBA\uDF93	\xF3\xBE\xAE\x93	ROCK IN HAND GAME		817	F488	EB83	7B68	EE88		86	F8F4	E693	7B68		16	F950	E010	7B68	 	 	
+FEB94	\uDBBA\uDF94	\xF3\xBE\xAE\x94	SCISSOR IN HAND GAME		319	F7C3	E5A6	7845	ECC3		87	F8F5	E694	7845		17	F951	E011	7845	 	 	
+FEB95	\uDBBA\uDF95	\xF3\xBE\xAE\x95	PAPER IN HAND GAME		320	F7C4	E5A7	7846	ECC4		88	F8F6	E695	7846		18	F952	E012	7846	 	 	
+FEB96	\uDBBA\uDF96	\xF3\xBE\xAE\x96	FISTED HAND		281	F6CC	E4F3	764E	EBCC		153	F9A2	E6FD	764E		13	F94D	E00D	764E	 	 	
+FEB97	\uDBBA\uDF97	\xF3\xBE\xAE\x97	THUMBS UP SIGN		287	F6D2	E4F9	7654	EBD2		328	F9CC	E727	7654		14	F94E	E00E	7654	 	 	
+FEB98	\uDBBA\uDF98	\xF3\xBE\xAE\x98	INDEX FINGER POINTING UPWARDS		284	F6CF	E4F6	7651	EBCF	[人差し指]		15	F94F	E00F	7651	 	 	
+FEB99	\uDBBA\uDF99	\xF3\xBE\xAE\x99	WHITE UP POINTING INDEX		390	F360	EA8D	7941	ED60	[↑]		226	F7CE	E22E	7941	 	 	 
+FEB9A	\uDBBA\uDF9A	\xF3\xBE\xAE\x9A	WHITE DOWN POINTING INDEX		391	F361	EA8E	7942	ED61	[↓]		227	F7CF	E22F	7942	 	 	 
+FEB9B	\uDBBA\uDF9B	\xF3\xBE\xAE\x9B	WHITE LEFT POINTING INDEX		140	F6D8	E4FF	765A	EBD8	[←]		228	F7D0	E230	765A	 	 	 
+FEB9C	\uDBBA\uDF9C	\xF3\xBE\xAE\x9C	WHITE RIGHT POINTING INDEX		141	F6D9	E500	765B	EBD9	[→]		229	F7D1	E231	765B	 	 	 
+FEB9D	\uDBBA\uDF9D	\xF3\xBE\xAE\x9D	WAVING HAND		463	F3AA	EAD6	7A2C	EDAA		88	F8F6	E695	7846		377	FB5E	E41E	7A2C	 	 	 
+FEB9E	\uDBBA\uDF9E	\xF3\xBE\xAE\x9E	CLAPPING HANDS		460	F3A7	EAD3	7A29	EDA7	[拍手]		378	FB5F	E41F	7A29	 	 	 
+FEB9F	\uDBBA\uDF9F	\xF3\xBE\xAE\x9F	OK HAND SIGN		461	F3A8	EAD4	7A2A	EDA8		135	F9B0	E70B	784C		379	FB60	E420	7A2A	 	 	 
+FEBA0	\uDBBA\uDFA0	\xF3\xBE\xAE\xA0	THUMBS DOWN SIGN		462	F3A9	EAD5	7A2B	EDA9		156	F9A5	E700	7A71		380	FB61	E421	7A2B	 	 	 
+FEBA1	\uDBBA\uDFA1	\xF3\xBE\xAE\xA1	OPEN HANDS		463	F3AA	EAD6	7A2C	EDAA		88	F8F6	E695	7846		381	FB62	E422	7D52	 	 	 
+FEBA2	\uDBBA\uDFA2	\xF3\xBE\xAE\xA2	ROCK ON	[ロックオン]	[ロックオン]	[ロックオン]	 		 
+FEE10	\uDBBB\uDE10	\xF3\xBE\xB8\x90	I-MODE	[iモード]		108	F975	E6D1	7E25	[iモード]	 	 	 
+FEE11	\uDBBB\uDE11	\xF3\xBE\xB8\x91	I-MODE WITH FRAME	[iモード]		109	F976	E6D2	7E26	[iモード]	 	 	 
+FEE12	\uDBBB\uDE12	\xF3\xBE\xB8\x92	PROVIDED BY DOCOMO	[ドコモ]		111	F978	E6D4	7E27	[ドコモ]	 	 	 
+FEE13	\uDBBB\uDE13	\xF3\xBE\xB8\x93	DOCOMO POINT	[ドコモポイント]		112	F979	E6D5	7E28	[ドコモポイント]	 	 	 
+FEE14	\uDBBB\uDE14	\xF3\xBE\xB8\x94	I-APPLI	[iアプリ]		301	F9B1	E70C	7E30	[iアプリ]	 	 	 
+FEE15	\uDBBB\uDE15	\xF3\xBE\xB8\x95	I-APPLI WITH BORDER	[iアプリ]		302	F9B2	E70D	7E31	[iアプリ]	 	 	 
+FEE16	\uDBBB\uDE16	\xF3\xBE\xB8\x96	PIAS PI	[ぴ]		177	F94A	E6A6	222E	[ぴ]	 	 	 
+FEE17	\uDBBB\uDE17	\xF3\xBE\xB8\x97	PIAS A	[あ]		178	F94B	E6A7	222E	[あ]	 	 	 
+FEE18	\uDBBB\uDE18	\xF3\xBE\xB8\x98	INVERSE TICKET	[チケット]		179	F94C	E6A8	222E	[チケット]	 	 	 
+FEE19	\uDBBB\uDE19	\xF3\xBE\xB8\x99	KATAKANA ABBREVIATION FOR TICKET	[チケット]		180	F94D	E6A9	222E	[チケット]	 	 	 
+FEE1A	\uDBBB\uDE1A	\xF3\xBE\xB8\x9A	RESERVE BY PHONE	[電話先行]		181	F94E	E6AA	222E	[電話先行]	 	 	 
+FEE1B	\uDBBB\uDE1B	\xF3\xBE\xB8\x9B	P CODE	[Pコード]		182	F94F	E6AB	222E	[Pコード]	 	 	 
+FEE1C	\uDBBB\uDE1C	\xF3\xBE\xB8\x9C	MOVIES 2		110	F6F0	E517	7672	EBF0		183	F953	E6AF	222E		61	F97D	E03D	7672	 	 	 
+FEE1D	\uDBBB\uDE1D	\xF3\xBE\xB8\x9D	PIAS PI INVERSE	[ぴ]		184	F954	E6B0	222E	[ぴ]	 	 	 
+FEE1E	\uDBBB\uDE1E	\xF3\xBE\xB8\x9E	PIAS PI CIRCLE	(ぴ)		185	F958	E6B4	222E	(ぴ)	 	 	 
+FEE1F	\uDBBB\uDE1F	\xF3\xBE\xB8\x9F	PIAS PI SQUARE	[ぴ]		186	F959	E6B5	222E	[ぴ]	 	 	 
+FEE20	\uDBBB\uDE20	\xF3\xBE\xB8\xA0	CHECK	[チェック]		187	F95A	E6B6	222E	[チェック]	 	 	 
+FEE21	\uDBBB\uDE21	\xF3\xBE\xB8\xA1	F	[F]		188	F95F	E6BB	222E	[F]	 	 	 
+FEE22	\uDBBB\uDE22	\xF3\xBE\xB8\xA2	D	[D]		189	F960	E6BC	222E	[D]	 	 	 
+FEE23	\uDBBB\uDE23	\xF3\xBE\xB8\xA3	S	[S]		190	F961	E6BD	222E	[S]	 	 	 
+FEE24	\uDBBB\uDE24	\xF3\xBE\xB8\xA4	C	[C]		191	F962	E6BE	222E	[C]	 	 	 
+FEE25	\uDBBB\uDE25	\xF3\xBE\xB8\xA5	R	[R]		192	F963	E6BF	222E	[R]	 	 	 
+FEE26	\uDBBB\uDE26	\xF3\xBE\xB8\xA6	BLACK AND WHITE SQUARE	■		193	F964	E6C0	222E	■	 	 	 
+FEE27	\uDBBB\uDE27	\xF3\xBE\xB8\xA7	BLACK SQUARE 3	■		194	F965	E6C1	222E	■	 	 	 
+FEE28	\uDBBB\uDE28	\xF3\xBE\xB8\xA8	DOWNWARD TRIANGLE	▼		195	F966	E6C2	222E	▼	 	 	 
+FEE29	\uDBBB\uDE29	\xF3\xBE\xB8\xA9	FOUR DAGGERS	††††		196	F967	E6C3	222E	††††	 	 	 
+FEE2A	\uDBBB\uDE2A	\xF3\xBE\xB8\xAA	THREE DAGGERS	†††		197	F968	E6C4	222E	†††	 	 	 
+FEE2B	\uDBBB\uDE2B	\xF3\xBE\xB8\xAB	TWO DAGGERS	††		198	F969	E6C5	222E	††	 	 	 
+FEE2C	\uDBBB\uDE2C	\xF3\xBE\xB8\xAC	DAGGER	†		199	F96A	E6C6	222E	†	 	 	 
+FEE2D	\uDBBB\uDE2D	\xF3\xBE\xB8\xAD	I	[I]		200	F96B	E6C7	222E	[I]	 	 	 
+FEE2E	\uDBBB\uDE2E	\xF3\xBE\xB8\xAE	M	[M]		201	F96C	E6C8	222E	[M]	 	 	 
+FEE2F	\uDBBB\uDE2F	\xF3\xBE\xB8\xAF	E	[E]		202	F96D	E6C9	222E	[E]	 	 	 
+FEE30	\uDBBB\uDE30	\xF3\xBE\xB8\xB0	VE	[VE]		203	F96E	E6CA	222E	[VE]	 	 	 
+FEE31	\uDBBB\uDE31	\xF3\xBE\xB8\xB1	SPHERE	●		204	F96F	E6CB	222E	●	 	 	 
+FEE32	\uDBBB\uDE32	\xF3\xBE\xB8\xB2	CREDIT CARDS NOT ACCEPTED	[カード使用不可]		205	F970	E6CC	222E	[カード使用不可]	 	 	 
+FEE33	\uDBBB\uDE33	\xF3\xBE\xB8\xB3	CHECKBOX		507	F7D9	EB02	785B	ECD9		206	F971	E6CD	222E	[チェックボックス]	 	 	 
+FEE40	\uDBBB\uDE40	\xF3\xBE\xB9\x80	EZ WEB		298	F794	E577	7774	EC94	[EZ]	[EZ]	 	 	 
+FEE41	\uDBBB\uDE41	\xF3\xBE\xB9\x81	EZ PLUS		331	F7CF	E5B2	7851	ECCF	[ezplus]	[ezplus]	 	 	 
+FEE42	\uDBBB\uDE42	\xF3\xBE\xB9\x82	EZ NAVIGATION		406	F370	EA9D	7951	ED70	[EZナビ]	[EZナビ]	 	 	 
+FEE43	\uDBBB\uDE43	\xF3\xBE\xB9\x83	EZ MOVIE		802	F478	EB74	7B59	EE78	[EZムービー]	[EZムービー]	 	 	 
+FEE44	\uDBBB\uDE44	\xF3\xBE\xB9\x84	CMAIL		815	F486	EB81	7B66	EE86	[Cメール]	[Cメール]	 	 	 
+FEE45	\uDBBB\uDE45	\xF3\xBE\xB9\x85	JAVA		823	F48E	EB89	7B6E	EE8E	[Java]	[Java]	 	 	 
+FEE46	\uDBBB\uDE46	\xF3\xBE\xB9\x86	BREW		824	F48F	EB8A	7B6F	EE8F	[BREW]	[BREW]	 	 	 
+FEE47	\uDBBB\uDE47	\xF3\xBE\xB9\x87	EZ RING MUSIC		825	F490	EB8B	7B70	EE90	[EZ着うた]	[EZ着うた]	 	 	 
+FEE48	\uDBBB\uDE48	\xF3\xBE\xB9\x88	EZ NAVI		826	F491	EB8C	7B71	EE91	[EZナビ]	[EZナビ]	 	 	 
+FEE49	\uDBBB\uDE49	\xF3\xBE\xB9\x89	WIN		827	F492	EB8D	7B72	EE92	[WIN]	[WIN]	 	 	 
+FEE4A	\uDBBB\uDE4A	\xF3\xBE\xB9\x8A	PREMIUM SIGN		828	F493	EB8E	7B73	EE93	[プレミアム]	[プレミアム]	 	 	 
+FEE70	\uDBBB\uDE70	\xF3\xBE\xB9\xB0	J-SKY1	〓	〓		479	FBD8	E538	222E	 	 	 
+FEE71	\uDBBB\uDE71	\xF3\xBE\xB9\xB1	J-SKY2	〓	〓		480	FBD9	E539	222E	 	 	 
+FEE72	\uDBBB\uDE72	\xF3\xBE\xB9\xB2	VODAFONE1	〓	〓		481	FBDA	E53A	7D73	 	 	 
+FEE73	\uDBBB\uDE73	\xF3\xBE\xB9\xB3	VODAFONE2	〓	〓		482	FBDB	E53B	7D74	 	 	 
+FEE74	\uDBBB\uDE74	\xF3\xBE\xB9\xB4	VODAFONE3	[v	[v		483	FBDC	E53C	7D75	 	 	 
+FEE75	\uDBBB\uDE75	\xF3\xBE\xB9\xB5	VODAFONE4	oda	oda		484	FBDD	E53D	7D76	 	 	 
+FEE76	\uDBBB\uDE76	\xF3\xBE\xB9\xB6	VODAFONE5	fone]	fone]		485	FBDE	E53E	7D77	 	 	 
+FEE77	\uDBBB\uDE77	\xF3\xBE\xB9\xB7	J-PHONE SHOP	〓	〓		264	F7F4	E254	222E	 	 	 
+FEE78	\uDBBB\uDE78	\xF3\xBE\xB9\xB8	SKY WEB	〓	〓		265	F7F5	E255	222E	 	 	 
+FEE79	\uDBBB\uDE79	\xF3\xBE\xB9\xB9	SKY WALKER	〓	〓		266	F7F6	E256	222E	 	 	 
+FEE7A	\uDBBB\uDE7A	\xF3\xBE\xB9\xBA	SKY MELODY	〓	〓		267	F7F7	E257	222E	 	 	 
+FEE7B	\uDBBB\uDE7B	\xF3\xBE\xB9\xBB	J-PHONE 1	〓	〓		268	F7F8	E258	222E	 	 	 
+FEE7C	\uDBBB\uDE7C	\xF3\xBE\xB9\xBC	J-PHONE 2	〓	〓		269	F7F9	E259	222E	 	 	 
+FEE7D	\uDBBB\uDE7D	\xF3\xBE\xB9\xBD	J-PHONE 3	〓	〓		270	F7FA	E25A	222E	 	 	 
+FEEA0	\uDBBB\uDEA0	\xF3\xBE\xBA\xA0	GOOGLE	[Google]	[Google]	[Google]		 	
diff --git a/legacy/include/animator/SkAnimator.h b/legacy/include/animator/SkAnimator.h
new file mode 100644
index 0000000..e6c5583
--- /dev/null
+++ b/legacy/include/animator/SkAnimator.h
@@ -0,0 +1,501 @@
+
+/*
+ * 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 SkAnimator_DEFINED
+#define SkAnimator_DEFINED
+
+#include "SkScalar.h"
+#include "SkKey.h"
+#include "SkEventSink.h"
+
+class SkAnimateMaker;
+class SkCanvas;
+class SkDisplayable;
+class SkEvent;
+class SkExtras;
+struct SkMemberInfo;
+class SkPaint;
+struct SkRect;
+class SkStream;
+class SkTypedArray;
+class SkXMLParserError;
+class SkDOM;
+struct SkDOMNode;
+
+/** SkElementType is the type of element: a rectangle, a color, an animator, and so on.
+    This enum is incomplete and will be fleshed out in a future release */
+enum SkElementType {
+    kElementDummyType
+};
+/** SkFieldType is the type of field: a scalar, a string, an integer, a boolean, and so on.
+    This enum is incomplete and will be fleshed out in a future release */
+enum SkFieldType {
+    kFieldDummyType
+};
+
+/** \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 
+    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 must contain an <event> element to draw. Usually, it contains
+    an <event kind="onload" /> block to add some drawing elements to the
+    display list when the document is first decoded.
+
+    Here's an "Hello World" XML sample:
+
+    <screenplay>
+        <event kind="onload" >
+            <text text="Hello World" y="20" />
+        </event>
+    </screenplay>
+
+    To read and draw this sample:
+
+        // choose one of these two
+        SkAnimator animator; // declare an animator instance on the stack
+    //  SkAnimator* animator = new SkAnimator() // or one could instantiate the class
+
+        // choose one of these three
+        animator.decodeMemory(buffer, size); // to read from RAM
+        animator.decodeStream(stream); // to read from a user-defined stream (e.g., a zip file)
+        animator.decodeURI(filename); // to read from a web location, or from a local text file
+
+        // to draw to the current window:
+        SkCanvas canvas(getBitmap()); // create a canvas
+        animator.draw(canvas, &paint, 0); // draw the scene
+*/
+class SkAnimator : public SkEventSink {
+public:
+    SkAnimator();
+    virtual ~SkAnimator();
+
+    /** 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);
+
+    /** Read in XML from a stream, and append it to the current
+        animator. Returns false if an error was encountered.
+        Error diagnostics are stored in fErrorCode and fLineNumber.
+        @param stream  The stream to append.
+        @return true if the XML was parsed successfully.
+    */
+    bool appendStream(SkStream* stream);
+
+    /** 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.
+        @param size  The XML text length in bytes.
+        @return true if the XML was parsed successfully.
+    */
+    bool decodeMemory(const void* buffer, size_t size);
+
+    /** 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.
+        @return true if the XML was parsed successfully.
+    */
+    virtual bool decodeStream(SkStream* stream);
+
+    /** 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 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).
+        @return true if the XML was parsed successfully.
+    */
+    bool decodeURI(const char uri[]);
+
+    /** 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" 
+            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 
+        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
+        down == 0, moved == 1, up == 2
+        @param x    The x-position of the mouse
+        @param y The y-position of the mouse
+        @return true if the event was dispatched successfully.
+    */
+    bool doClickEvent(int state, SkScalar x, SkScalar y);
+
+    /** 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" 
+            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 
+        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. 
+    */
+    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 
+        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 
+        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 
+        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 
+        redraw area.
+    */
+    DifferenceType draw(SkCanvas* canvas, SkMSec time);
+
+    /** Experimental:
+        Helper to choose whether to return a SkView::Click handler.
+        @param x ignored
+        @param y ignored
+        @return true if a mouseDown event handler is enabled.
+    */
+    bool findClickEvent(SkScalar x, SkScalar y); 
+
+
+    /** Get the nested animator associated with this element, if any.
+        Use this to access a movie's event sink, to send events to movies.
+        @param element the value returned by getElement
+        @return the internal animator.
+    */
+    const SkAnimator* getAnimator(const SkDisplayable* element) const;
+
+    /** Returns the scalar value of the specified element's attribute[index]
+        @param element the value returned by getElement
+        @param field the value returned by getField
+        @param index the array entry
+        @return the integer value to retrieve, or SK_NaN32 if unsuccessful
+    */
+    int32_t getArrayInt(const SkDisplayable* element, const SkMemberInfo* field, int index);
+
+    /** 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 index the array entry
+        @return the integer value to retrieve, or SK_NaN32 if unsuccessful
+    */
+    int32_t getArrayInt(const char* elementID, const char* fieldName, int index);
+
+    /** Returns the scalar value of the specified element's attribute[index]
+        @param element the value returned by getElement
+        @param field the value returned by getField
+        @param index the array entry
+        @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful
+    */
+    SkScalar getArrayScalar(const SkDisplayable* element, const SkMemberInfo* field, int index);
+
+    /** 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 index the array entry
+        @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful
+    */
+    SkScalar getArrayScalar(const char* elementID, const char* fieldName, int index);
+
+    /** 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 index the array entry
+        @return the string value to retrieve, or null if unsuccessful
+    */
+    const char* getArrayString(const SkDisplayable* element, const SkMemberInfo* field, int index);
+
+    /** 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 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 
+        @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  
+        @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 
+        @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  
+        @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  
+        @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  
+        @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  
+        @return the attribute type, or 0 if the element can't be found
+    */
+    SkFieldType getFieldType(const char* elementID, const char* fieldName);
+
+    /** Returns the recommended animation interval. Returns zero if no
+        interval is specified.
+    */
+    SkMSec getInterval();
+
+    /** Returns the partial rectangle to invalidate after drawing. Call after draw() returns
+    kIsPartiallyDifferent to do a mimimal inval(). */
+    void getInvalBounds(SkRect* inval); 
+
+    /** 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. 
+    */
+    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  
+        @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  
+        @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  
+        @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  
+        @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  
+        @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  
+        @return the string value to retrieve, or null if not found
+    */
+    const char* getString(const char* elementID, const char* fieldName);
+
+    /** Gets the file default directory of the URL base path set explicitly or by reading the last URL. */
+    const char* getURIBase();
+
+    /** 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 
+        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 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 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 data the integer value to set
+        @return true if the value was set successfully
+    */
+    bool setInt(const char* elementID, const char* fieldName, int32_t data);
+
+    /** 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 data the scalar value to set
+        @return true if the value was set successfully
+    */
+    bool setScalar(const char* elementID, const char* fieldName, SkScalar data);
+
+    /** 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 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 
+    */
+    void setURIBase(const char* path);
+
+    typedef void* Handler;
+    // This guy needs to be exported to java, so don't make it virtual
+    void setHostHandler(Handler handler) {
+        this->onSetHostHandler(handler);
+    }
+
+    /** \class Timeline
+    Returns current time to animator. To return a custom timeline, create a child
+    class and override the getMSecs method.
+    */
+    class Timeline {
+    public:
+        virtual ~Timeline() {}
+
+        /** Returns the current time in milliseconds */
+        virtual SkMSec getMSecs() const = 0;
+    };
+
+    /** 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
+    */
+    void setTimeline(const Timeline& );
+
+    static void Init(bool runUnitTests);
+    static void Term();
+    
+    /** 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  
+    
+protected:
+    virtual void onSetHostHandler(Handler handler);
+    virtual void onEventPost(SkEvent*, SkEventSinkID);
+    virtual void onEventPostTime(SkEvent*, SkEventSinkID, SkMSec time);
+
+private:
+// helper functions for setters
+    bool setArray(SkDisplayable* element, const SkMemberInfo* field, SkTypedArray array);
+    bool setArray(const char* elementID, const char* fieldName, SkTypedArray array);
+    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;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript2;
+    friend class SkApply;
+    friend class SkDisplayMovie;
+    friend class SkDisplayType;
+    friend class SkPost;
+    friend class SkXMLAnimatorWriter;
+};
+
+#endif
+
diff --git a/legacy/include/animator/SkAnimatorView.h b/legacy/include/animator/SkAnimatorView.h
new file mode 100644
index 0000000..940dd26
--- /dev/null
+++ b/legacy/include/animator/SkAnimatorView.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 SkAnimatorView_DEFINED
+#define SkAnimatorView_DEFINED
+
+#include "SkView.h"
+#include "SkAnimator.h"
+
+class SkAnimatorView : public SkView {
+public:
+            SkAnimatorView();
+    virtual ~SkAnimatorView();
+
+    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 bool onEvent(const SkEvent&);
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+    SkAnimator* fAnimator;
+
+    typedef SkView INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/config/sk_stdint.h b/legacy/include/config/sk_stdint.h
new file mode 100644
index 0000000..360755e
--- /dev/null
+++ b/legacy/include/config/sk_stdint.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 sk_stdint_DEFINED
+#define sk_stdint_DEFINED
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned uint32_t;
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+#endif
diff --git a/legacy/include/core/Sk64.h b/legacy/include/core/Sk64.h
new file mode 100644
index 0000000..b86e0be
--- /dev/null
+++ b/legacy/include/core/Sk64.h
@@ -0,0 +1,231 @@
+
+/*
+ * 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 Sk64_DEFINED
+#define Sk64_DEFINED
+
+#include "SkFixed.h"
+
+/** \class Sk64
+
+    Sk64 is a 64-bit math package that does not require long long support from the compiler.
+*/
+struct SK_API Sk64 {
+    int32_t  fHi;   //!< the high 32 bits of the number (including sign)
+    uint32_t fLo;   //!< the low 32 bits of the number
+
+    /** Returns non-zero if the Sk64 can be represented as a signed 32 bit integer
+    */
+    SkBool is32() const { return fHi == ((int32_t)fLo >> 31); }
+
+    /** Returns non-zero if the Sk64 cannot be represented as a signed 32 bit integer
+    */
+    SkBool is64() const { return fHi != ((int32_t)fLo >> 31); }
+
+    /** Returns non-zero if the Sk64 can be represented as a signed 48 bit integer. Used to know
+        if we can shift the value down by 16 to treat it as a SkFixed.
+    */
+    SkBool isFixed() const;
+
+    /** Return the signed 32 bit integer equivalent. Asserts that is32() returns non-zero.
+    */
+    int32_t get32() const { SkASSERT(this->is32()); return (int32_t)fLo; }
+
+    /** Return the number >> 16. Asserts that this does not loose any significant high bits.
+    */
+    SkFixed getFixed() const {
+        SkASSERT(this->isFixed());
+
+        uint32_t sum = fLo + (1 << 15);
+        int32_t  hi = fHi;
+        if (sum < fLo) {
+            hi += 1;
+        }
+        return (hi << 16) | (sum >> 16);
+    }
+
+    /** Return the number >> 30. Asserts that this does not loose any
+        significant high bits.
+    */
+    SkFract getFract() const;
+
+    /** Returns the square-root of the number as a signed 32 bit value. */
+    int32_t getSqrt() const;
+
+    /** Returns the number of leading zeros of the absolute value of this.
+        Will return in the range [0..64]
+    */
+    int getClzAbs() const;
+
+    /** Returns non-zero if the number is zero */
+    SkBool  isZero() const { return (fHi | fLo) == 0; }
+
+    /** Returns non-zero if the number is non-zero */
+    SkBool  nonZero() const { return fHi | fLo; }
+
+    /** Returns non-zero if the number is negative (number < 0) */
+    SkBool  isNeg() const { return (uint32_t)fHi >> 31; }
+
+    /** Returns non-zero if the number is positive (number > 0) */
+    SkBool  isPos() const { return ~(fHi >> 31) & (fHi | fLo); }
+
+    /** Returns -1,0,+1 based on the sign of the number */
+    int     getSign() const { return (fHi >> 31) | Sk32ToBool(fHi | fLo); }
+
+    /** Negate the number */
+    void    negate();
+
+    /** If the number < 0, negate the number
+    */
+    void    abs();
+
+    /** Returns the number of bits needed to shift the Sk64 to the right
+        in order to make it fit in a signed 32 bit integer.
+    */
+    int     shiftToMake32() const;
+
+    /** Set the number to zero */
+    void    setZero() { fHi = fLo = 0; }
+
+    /** Set the high and low 32 bit values of the number */
+    void    set(int32_t hi, uint32_t lo) { fHi = hi; fLo = lo; }
+
+    /** Set the number to the specified 32 bit integer */
+    void    set(int32_t a) { fHi = a >> 31; fLo = a; }
+
+    /** Set the number to the product of the two 32 bit integers */
+    void    setMul(int32_t a, int32_t b);
+
+    /** extract 32bits after shifting right by bitCount.
+        Note: itCount must be [0..63].
+        Asserts that no significant high bits were lost.
+    */
+    int32_t getShiftRight(unsigned bitCount) const;
+
+    /** Shift the number left by the specified number of bits.
+        @param bits How far to shift left, must be [0..63]
+    */
+    void    shiftLeft(unsigned bits);
+
+    /** Shift the number right by the specified number of bits.
+        @param bits How far to shift right, must be [0..63]. This
+        performs an arithmetic right-shift (sign extending).
+    */
+    void    shiftRight(unsigned bits);
+
+    /** Shift the number right by the specified number of bits, but
+        round the result.
+        @param bits How far to shift right, must be [0..63]. This
+        performs an arithmetic right-shift (sign extending).
+    */
+    void    roundRight(unsigned bits);
+
+    /** Add the specified 32 bit integer to the number */
+    void add(int32_t lo) {
+        int32_t  hi = lo >> 31; // 0 or -1
+        uint32_t sum = fLo + (uint32_t)lo;
+
+        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;
+
+        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);
+
+    enum DivOptions {
+        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).
+    */
+    void    div(int32_t, DivOptions);
+
+    /** return (this + other >> 16) as a 32bit result */
+    SkFixed addGetFixed(const Sk64& other) const {
+        return this->addGetFixed(other.fHi, other.fLo);
+    }
+
+    /** return (this + Sk64(hi, lo) >> 16) as a 32bit result */
+    SkFixed addGetFixed(int32_t hi, uint32_t lo) const {
+#ifdef SK_DEBUG
+        Sk64    tmp(*this);
+        tmp.add(hi, lo);
+#endif
+
+        uint32_t sum = fLo + lo;
+        hi += fHi + (sum < fLo);
+        lo = sum;
+
+        sum = lo + (1 << 15);
+        if (sum < lo)
+            hi += 1;
+
+        hi = (hi << 16) | (sum >> 16);
+        SkASSERT(hi == tmp.getFixed());
+        return hi;
+    }
+
+    /** Return the result of dividing the number by denom, treating the answer
+        as a SkFixed. (*this) << 16 / denom. It is an error for denom to be 0.
+    */
+    SkFixed getFixedDiv(const Sk64& denom) const;
+
+    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.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);
+    }
+
+#ifdef SkLONGLONG
+    SkLONGLONG getLongLong() const;
+#endif
+};
+
+#endif
+
diff --git a/legacy/include/core/SkAdvancedTypefaceMetrics.h b/legacy/include/core/SkAdvancedTypefaceMetrics.h
new file mode 100755
index 0000000..09fc9a9
--- /dev/null
+++ b/legacy/include/core/SkAdvancedTypefaceMetrics.h
@@ -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.
+ */
+
+
+#ifndef SkAdvancedTypefaceMetrics_DEFINED
+#define SkAdvancedTypefaceMetrics_DEFINED
+
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+#include "SkTemplates.h"
+#include "SkTScopedPtr.h"
+
+/** \class SkAdvancedTypefaceMetrics
+
+    The SkAdvancedTypefaceMetrics class is used by the PDF backend to correctly
+    embed typefaces.  This class is filled in with information about a given
+    typeface by the SkFontHost class.
+*/
+
+class SkAdvancedTypefaceMetrics : public SkRefCnt {
+public:
+    SkString fFontName;
+
+    enum FontType {
+        kType1_Font,
+        kType1CID_Font,
+        kCFF_Font,
+        kTrueType_Font,
+        kOther_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
+    // kNotEmbeddable_Font, the per glyph information will never be populated.
+    FontType fType;
+
+    // fMultiMaster may be true for Type1_Font or CFF_Font.
+    bool fMultiMaster;
+    uint16_t fLastGlyphID; // The last valid glyph ID in the font.
+    uint16_t fEmSize;  // The size of the em box (defines font units).
+
+    // These enum values match the values used in the PDF file format.
+    enum StyleFlags {
+        kFixedPitch_Style  = 0x00001,
+        kSerif_Style       = 0x00002,
+        kSymbolic_Style    = 0x00004,
+        kScript_Style      = 0x00008,
+        kNonsymbolic_Style = 0x00020,
+        kItalic_Style      = 0x00040,
+        kAllCaps_Style     = 0x10000,
+        kSmallCaps_Style   = 0x20000,
+        kForceBold_Style   = 0x40000,
+    };
+    uint16_t fStyle;        // Font style characteristics.
+    int16_t fItalicAngle;   // Counterclockwise degrees from vertical of the
+                            // dominant vertical stroke for an Italic face.
+    // The following fields are all in font units.
+    int16_t fAscent;       // Max height above baseline, not including accents.
+    int16_t fDescent;      // Max depth below baseline (negative).
+    int16_t fStemV;        // Thickness of dominant vertical stem.
+    int16_t fCapHeight;    // Height (from baseline) of top of flat capitals.
+
+    SkIRect fBBox;  // The bounding box of all glyphs (in font units).
+
+    // The type of advance data wanted.
+    enum PerGlyphInfo {
+      kNo_PerGlyphInfo         = 0x0, // Don't populate any per glyph info.
+      kHAdvance_PerGlyphInfo   = 0x1, // Populate horizontal advance data.
+      kVAdvance_PerGlyphInfo   = 0x2, // Populate vertical advance data.
+      kGlyphNames_PerGlyphInfo = 0x4, // Populate glyph names (Type 1 only).
+      kToUnicode_PerGlyphInfo  = 0x8, // Populate ToUnicode table, ignored
+                                      // for Type 1 fonts
+    };
+
+    template <typename Data>
+    struct AdvanceMetric {
+        enum MetricType {
+            kDefault,  // Default advance: fAdvance.count = 1
+            kRange,    // Advances for a range: fAdvance.count = fEndID-fStartID
+            kRun,      // fStartID-fEndID have same advance: fAdvance.count = 1
+        };
+        MetricType fType;
+        uint16_t fStartId;
+        uint16_t fEndId;
+        SkTDArray<Data> fAdvance;
+        SkTScopedPtr<AdvanceMetric<Data> > fNext;
+    };
+
+    struct VerticalMetric {
+        int16_t fVerticalAdvance;
+        int16_t fOriginXDisp;  // Horiz. displacement of the secondary origin.
+        int16_t fOriginYDisp;  // Vert. displacement of the secondary origin.
+    };
+    typedef AdvanceMetric<int16_t> WidthRange;
+    typedef AdvanceMetric<VerticalMetric> VerticalAdvanceRange;
+
+    // This is indexed by glyph id.
+    SkTScopedPtr<WidthRange> fGlyphWidths;
+    // Only used for Vertical CID fonts.
+    SkTScopedPtr<VerticalAdvanceRange> fVerticalMetrics;
+
+    // The names of each glyph, only populated for postscript fonts.
+    SkTScopedPtr<SkAutoTArray<SkString> > fGlyphNames;
+
+    // The mapping from glyph to Unicode, only populated if
+    // kToUnicode_PerGlyphInfo is passed to GetAdvancedTypefaceMetrics.
+    SkTDArray<SkUnichar> fGlyphToUnicode;
+};
+
+namespace skia_advanced_typeface_metrics_utils {
+
+template <typename Data>
+void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
+                       int startId);
+
+template <typename Data>
+SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange(
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot,
+        int startId);
+
+template <typename Data>
+void finishRange(
+        SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
+        int endId,
+        typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType
+                type);
+
+/** Retrieve advance data for glyphs. Used by the PDF backend. It calls
+    underlying platform dependent API getAdvance to acquire the data.
+    @param num_glyphs    Total number of glyphs in the given font.
+    @param glyphIDs      For per-glyph info, specify subset of the font by
+                         giving glyph ids.  Each integer represents a glyph
+                         id.  Passing NULL means all glyphs in the font.
+    @param glyphIDsCount Number of elements in subsetGlyphIds. Ignored if
+                         glyphIDs is NULL.
+*/
+template <typename Data, typename FontHandle>
+SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
+        FontHandle fontHandle,
+        int num_glyphs,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount,
+        bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data));
+
+} // namespace skia_advanced_typeface_metrics_utils
+
+#endif
diff --git a/include/core/SkAutoKern.h b/legacy/include/core/SkAutoKern.h
similarity index 100%
rename from include/core/SkAutoKern.h
rename to legacy/include/core/SkAutoKern.h
diff --git a/legacy/include/core/SkBitmap.h b/legacy/include/core/SkBitmap.h
new file mode 100644
index 0000000..2d5fc41
--- /dev/null
+++ b/legacy/include/core/SkBitmap.h
@@ -0,0 +1,850 @@
+
+/*
+ * 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 SkBitmap_DEFINED
+#define SkBitmap_DEFINED
+
+#include "Sk64.h"
+#include "SkColor.h"
+#include "SkPoint.h"
+#include "SkRefCnt.h"
+
+struct SkIRect;
+class SkColorTable;
+class SkPaint;
+class SkPixelRef;
+class SkRegion;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+// This is an opaque class, not interpreted by skia
+class SkGpuTexture;
+
+/** \class SkBitmap
+
+    The SkBitmap class specifies a raster bitmap. A bitmap has an integer width
+    and height, and a format (config), and a pointer to the actual pixels.
+    Bitmaps can be drawn into a SkCanvas, but they are also used to specify the
+    target of a SkCanvas' drawing operations.
+    A const SkBitmap exposes getAddr(), which lets a caller write its pixels;
+    the constness is considered to apply to the bitmap's configuration, not
+    its contents.
+*/
+class SK_API SkBitmap {
+public:
+    class Allocator;
+
+    enum Config {
+        kNo_Config,         //!< bitmap has not been configured
+        /**
+         *  1-bit per pixel, (0 is transparent, 1 is opaque)
+         *  Valid as a destination (target of a canvas), but not valid as a src.
+         *  i.e. you can draw into a 1-bit bitmap, but you cannot draw from one.
+         */
+        kA1_Config,
+        kA8_Config,         //!< 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque)
+        kIndex8_Config,     //!< 8-bits per pixel, using SkColorTable to specify the colors
+        kRGB_565_Config,    //!< 16-bits per pixel, (see SkColorPriv.h for packing)
+        kARGB_4444_Config,  //!< 16-bits per pixel, (see SkColorPriv.h for packing)
+        kARGB_8888_Config,  //!< 32-bits per pixel, (see SkColorPriv.h for packing)
+        /**
+         *  Custom compressed format, not supported on all platforms.
+         *  Cannot be used as a destination (target of a canvas).
+         *  i.e. you may be able to draw from one, but you cannot draw into one.
+         */
+        kRLE_Index8_Config,
+
+        kConfigCount
+    };
+
+    /** 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.
+    */
+    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
+        with the src bitmap.
+    */
+    SkBitmap& operator=(const SkBitmap& src);
+    /** Swap the fields of the two bitmaps. This routine is guaranteed to never fail or throw.
+    */
+    //  This method is not exported to java.
+    void swap(SkBitmap& other);
+
+    /** Return true iff the bitmap has empty dimensions.
+    */
+    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()).
+    */
+    bool isNull() const { return NULL == fPixels && NULL == fPixelRef; }
+
+    /** Return the config for the bitmap.
+    */
+    Config  config() const { return (Config)fConfig; }
+    /** DEPRECATED, use config()
+    */
+    Config  getConfig() const { return this->config(); }
+    /** Return the bitmap's width, in pixels.
+    */
+    int width() const { return fWidth; }
+    /** Return the bitmap's height, in pixels.
+    */
+    int height() const { return fHeight; }
+    /** Return the number of bytes between subsequent rows of the bitmap.
+    */
+    int rowBytes() const { return fRowBytes; }
+
+    /** Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for
+        2-bytes per pixel configs, 2 for 4-bytes per pixel configs). Return 0
+        for configs that are not at least 1-byte per pixel (e.g. kA1_Config
+        or kNo_Config)
+    */
+    int shiftPerPixel() const { return fBytesPerPixel >> 1; }
+
+    /** Return the number of bytes per pixel based on the config. If the config
+        does not have at least 1 byte per (e.g. kA1_Config) then 0 is returned.
+    */
+    int bytesPerPixel() const { return fBytesPerPixel; }
+
+    /** Return the rowbytes expressed as a number of pixels (like width and
+        height). Note, for 1-byte per pixel configs like kA8_Config, this will
+        return the same as rowBytes(). Is undefined for configs that are less
+        than 1-byte per pixel (e.g. kA1_Config)
+    */
+    int rowBytesAsPixels() const { return fRowBytes >> (fBytesPerPixel >> 1); }
+
+    /** Return the address of the pixels for this SkBitmap.
+    */
+    void* getPixels() const { return fPixels; }
+
+    /** Return the byte size of the pixels, based on the height and rowBytes.
+        Note this truncates the result to 32bits. Call getSize64() to detect
+        if the real size exceeds 32bits.
+    */
+    size_t getSize() const { return fHeight * fRowBytes; }
+
+    /** 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.
+    */
+    size_t getSafeSize() const ;
+
+    /** Return the byte size of the pixels, based on the height and rowBytes.
+        This routine is slightly slower than getSize(), but does not truncate
+        the answer to 32bits.
+    */
+    Sk64 getSize64() const {
+        Sk64 size;
+        size.setMul(fHeight, fRowBytes);
+        return size;
+    }
+
+    /** Same as getSafeSize(), but does not truncate the answer to 32bits.
+    */
+    Sk64 getSafeSize64() const ;
+
+    /** Returns true if this bitmap is marked as immutable, meaning that the
+        contents of its pixels will not change for the lifetime of the bitmap.
+    */
+    bool isImmutable() const;
+
+    /** 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 
+        cleared once it is set. This state propagates to all other bitmaps
+        that share the same pixelref.
+    */
+    void setImmutable();
+
+    /** Returns true if the bitmap is opaque (has no translucent/transparent pixels).
+    */
+    bool isOpaque() const;
+
+    /** Specify if this bitmap's pixels are all opaque or not. Is only meaningful for configs
+        that support per-pixel alpha (RGB32, A1, A8).
+    */
+    void setIsOpaque(bool);
+
+    /** Returns true if the bitmap is volatile (i.e. should not be cached by devices.)
+    */
+    bool isVolatile() const;
+
+    /** 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 
+        consumption on the device.
+    */
+    void setIsVolatile(bool);
+
+    /** Reset the bitmap to its initial state (see default constructor). If we are a (shared)
+        owner of the pixels, that ownership is decremented.
+    */
+    void reset();
+
+    /** Given a config and a width, this computes the optimal rowBytes value. This is called automatically
+        if you pass 0 for rowBytes to setConfig().
+    */
+    static int ComputeRowBytes(Config c, int width);
+
+    /** Return the bytes-per-pixel for the specified config. If the config is
+        not at least 1-byte per pixel, return 0, including for kNo_Config.
+    */
+    static int ComputeBytesPerPixel(Config c);
+
+    /** Return the shift-per-pixel for the specified config. If the config is
+     not at least 1-byte per pixel, return 0, including for kNo_Config.
+     */
+    static int ComputeShiftPerPixel(Config c) {
+        return ComputeBytesPerPixel(c) >> 1;
+    }
+
+    static Sk64 ComputeSize64(Config, int width, int height);
+    static size_t ComputeSize(Config, int width, int height);
+
+    /** 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().
+    */
+    void setConfig(Config, int width, int height, int rowBytes = 0);
+    /** Use this to assign a new pixel address for an existing bitmap. This
+        will automatically release any pixelref previously installed. Only call
+        this if you are handling ownership/lifetime of the pixel memory.
+
+        If the bitmap retains a reference to the colortable (assuming it is
+        not null) it will take care of incrementing the reference count.
+
+        @param pixels   Address for the pixels, managed by the caller.
+        @param ctable   ColorTable (or null) that matches the specified pixels
+    */
+    void setPixels(void* p, SkColorTable* ctable = NULL);
+
+    /** Copies the bitmap's pixels to the location pointed at by dst and returns
+        true if possible, returns false otherwise.
+
+        In the case when the dstRowBytes matches the bitmap's rowBytes, the copy
+        may be made faster by copying over the dst's per-row padding (for all
+        rows but the last). By setting preserveDstPad to true the caller can
+        disable this optimization and ensure that pixels in the padding are not
+        overwritten.
+
+        Always returns false for RLE formats.
+
+        @param dst      Location of destination buffer.
+        @param dstSize  Size of destination buffer. Must be large enough to hold
+                        pixels using indicated stride.
+        @param dstRowBytes  Width of each line in the buffer. If -1, uses
+                            bitmap's internal stride.
+        @param preserveDstPad Must we preserve padding in the dst
+    */
+    bool copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes = -1,
+                      bool preserveDstPad = false)
+         const;
+
+    /** Use the standard HeapAllocator to create the pixelref that manages the
+        pixel memory. It will be sized based on the current width/height/config.
+        If this is called multiple times, a new pixelref object will be created
+        each time.
+
+        If the bitmap retains a reference to the colortable (assuming it is
+        not null) it will take care of incrementing the reference count.
+
+        @param ctable   ColorTable (or null) to use with the pixels that will
+                        be allocated. Only used if config == Index8_Config
+        @return true if the allocation succeeds. If not the pixelref field of
+                     the bitmap will be unchanged.
+    */
+    bool allocPixels(SkColorTable* ctable = NULL) {
+        return this->allocPixels(NULL, ctable);
+    }
+
+    /** Use the specified Allocator to create the pixelref that manages the
+        pixel memory. It will be sized based on the current width/height/config.
+        If this is called multiple times, a new pixelref object will be created
+        each time.
+
+        If the bitmap retains a reference to the colortable (assuming it is
+        not null) it will take care of incrementing the reference count.
+
+        @param allocator The Allocator to use to create a pixelref that can
+                         manage the pixel memory for the current
+                         width/height/config. If allocator is NULL, the standard
+                         HeapAllocator will be used.
+        @param ctable   ColorTable (or null) to use with the pixels that will
+                        be allocated. Only used if config == Index8_Config.
+                        If it is non-null and the config is not Index8, it will
+                        be ignored.
+        @return true if the allocation succeeds. If not the pixelref field of
+                     the bitmap will be unchanged.
+    */
+    bool allocPixels(Allocator* allocator, SkColorTable* ctable);
+
+    /** Return the current pixelref object, if any
+    */
+    SkPixelRef* pixelRef() const { return fPixelRef; }
+    /** Return the offset into the pixelref, if any. Will return 0 if there is
+        no pixelref installed.
+    */
+    size_t pixelRefOffset() const { return fPixelRefOffset; }
+    /** Assign a pixelref and optional offset. Pixelrefs are reference counted,
+        so the existing one (if any) will be unref'd and the new one will be
+        ref'd.
+    */
+    SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset = 0);
+
+    /** Call this to ensure that the bitmap points to the current pixel address
+        in the pixelref. Balance it with a call to unlockPixels(). These calls
+        are harmless if there is no pixelref.
+    */
+    void lockPixels() const;
+    /** When you are finished access the pixel memory, call this to balance a
+        previous call to lockPixels(). This allows pixelrefs that implement
+        cached/deferred image decoding to know when there are active clients of
+        a given image.
+    */
+    void unlockPixels() const;
+
+    /**
+     *  Some bitmaps can return a copy of their pixels for lockPixels(), but
+     *  that copy, if modified, will not be pushed back. These bitmaps should
+     *  not be used as targets for a raster device/canvas (since all pixels
+     *  modifications will be lost when unlockPixels() is called.)
+     */
+    bool lockPixelsAreWritable() const;
+
+    /** Call this to be sure that the bitmap is valid enough to be drawn (i.e.
+        it has non-null pixels, and if required by its config, it has a
+        non-null colortable. Returns true if all of the above are met.
+    */
+    bool readyToDraw() const {
+        return this->getPixels() != NULL &&
+               ((this->config() != kIndex8_Config &&
+                 this->config() != kRLE_Index8_Config) ||
+                       fColorTable != NULL);
+    }
+
+    /** Returns the pixelRef's texture, or NULL
+     */
+    SkGpuTexture* getTexture() const;
+
+    /** Return the bitmap's colortable (if any). Does not affect the colortable's
+        reference count.
+    */
+    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.
+    */
+    uint32_t getGenerationID() const;
+
+    /** Call this if you have changed the contents of the pixels. This will in-
+        turn cause a different generation ID value to be returned from
+        getGenerationID().
+    */
+    void notifyPixelsChanged() const;
+
+    /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format
+        for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is ignored.
+        If the config is kA8_Config, then the r,g,b parameters are ignored.
+    */
+    void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const;
+    /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format
+        for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is presumed
+        to be 0xFF. If the config is kA8_Config, then the r,g,b parameters are ignored and the
+        pixels are all set to 0xFF.
+    */
+    void eraseRGB(U8CPU r, U8CPU g, U8CPU b) const {
+        this->eraseARGB(0xFF, r, g, b);
+    }
+    /** Initialize the bitmap's pixels with the specified color, automatically converting into the correct format
+        for the bitmap's config. If the config is kRGB_565_Config, then the color's alpha value is presumed
+        to be 0xFF. If the config is kA8_Config, then only the color's alpha value is used.
+    */
+    void eraseColor(SkColor c) const {
+        this->eraseARGB(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c),
+                        SkColorGetB(c));
+    }
+
+    /** 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).
+
+        @param subset The subset of the bitmap to scroll/move. To scroll the
+                      entire contents, specify [0, 0, width, height] or just
+                      pass null.
+        @param dx The amount to scroll in X
+        @param dy The amount to scroll in Y
+        @param inval Optional (may be null). Returns the area of the bitmap that
+                     was scrolled away. E.g. if dx = dy = 0, then inval would
+                     be set to empty. If dx >= width or dy >= height, then
+                     inval would be set to the entire bounds of the bitmap.
+        @return true if the scroll was doable. Will return false if the bitmap
+                     uses an unsupported config for scrolling (only kA8,
+                     kIndex8, kRGB_565, kARGB_4444, kARGB_8888 are supported).
+                     If no pixels are present (i.e. getPixels() returns false)
+                     inval will still be updated, and true will be returned.
+    */
+    bool scrollRect(const SkIRect* subset, int dx, int dy,
+                    SkRegion* inval = NULL) const;
+
+    /**
+     *  Return the SkColor of the specified pixel.  In most cases this will
+     *  require un-premultiplying the color.  Alpha only configs (A1 and A8)
+     *  return black with the appropriate alpha set.  The value is undefined
+     *  for kNone_Config or if x or y are out of bounds, or if the bitmap
+     *  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
+        check happens at runtime, it is much slower than using a size-specific
+        version. Unlike the size-specific methods, this routine also checks if
+        getPixels() returns null, and returns that. The size-specific routines
+        perform a debugging assert that getPixels() is not null, but they do
+        not do any runtime checks.
+    */
+    void* getAddr(int x, int y) const;
+
+    /** Returns the address of the pixel specified by x,y for 32bit pixels.
+     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  and that the config is 32-bit, however none of these checks are performed
+     *  in the release build.
+     */
+    inline uint32_t* getAddr32(int x, int y) const;
+
+    /** Returns the address of the pixel specified by x,y for 16bit pixels.
+     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  and that the config is 16-bit, however none of these checks are performed
+     *  in the release build.
+     */
+    inline uint16_t* getAddr16(int x, int y) const;
+
+    /** Returns the address of the pixel specified by x,y for 8bit pixels.
+     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  and that the config is 8-bit, however none of these checks are performed
+     *  in the release build.
+     */
+    inline uint8_t* getAddr8(int x, int y) const;
+
+    /** Returns the address of the byte containing the pixel specified by x,y
+     *  for 1bit pixels.
+     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  and that the config is 1-bit, however none of these checks are performed
+     *  in the release build.
+     */
+    inline uint8_t* getAddr1(int x, int y) const;
+
+    /** Returns the color corresponding to the pixel specified by x,y for
+     *  colortable based bitmaps.
+     *  In debug build, this asserts that the pixels are allocated and locked,
+     *  that the config is kIndex8, and that the colortable is allocated,
+     *  however none of these checks are performed in the release build.
+     */
+    inline SkPMColor getIndex8Color(int x, int y) const;
+
+    /** Set dst to be a setset of this bitmap. If possible, it will share the
+        pixel memory, and just point into a subset of it. However, if the config
+        does not support this, a local copy will be made and associated with
+        the dst bitmap. If the subset rectangle, intersected with the bitmap's
+        dimensions is empty, or if there is an unsupported config, false will be
+        returned and dst will be untouched.
+        @param dst  The bitmap that will be set to a subset of this bitmap
+        @param subset The rectangle of pixels in this bitmap that dst will
+                      reference.
+        @return true if the subset copy was successfully made.
+    */
+    bool extractSubset(SkBitmap* dst, const SkIRect& subset) const;
+
+    /** Makes a deep copy of this bitmap, respecting the requested config,
+     *  and allocating the dst pixels on the cpu.
+     *  Returns false if either there is an error (i.e. the src does not have
+     *  pixels) or the request cannot be satisfied (e.g. the src has per-pixel
+     *  alpha, and the requested config does not support alpha).
+     *  @param dst The bitmap to be sized and allocated
+     *  @param c The desired config for dst
+     *  @param allocator Allocator used to allocate the pixelref for the dst
+     *                   bitmap. If this is null, the standard HeapAllocator
+     *                   will be used.
+     *  @return true if the copy could be made.
+     */
+    bool copyTo(SkBitmap* dst, Config c, Allocator* allocator = NULL) const;
+
+    /** Makes a deep copy of this bitmap, respecting the requested config, and
+     *  with custom allocation logic that will keep the copied pixels
+     *  in the same domain as the source: If the src pixels are allocated for
+     *  the cpu, then so will the dst. If the src pixels are allocated on the
+     *  gpu (typically as a texture), the it will do the same for the dst.
+     *  If the request cannot be fulfilled, returns false and dst is unmodified.
+     */
+    bool deepCopyTo(SkBitmap* dst, Config c) const;
+
+    /** Returns true if this bitmap can be deep copied into the requested config
+        by calling copyTo().
+     */
+    bool canCopyTo(Config newConfig) const;
+
+    bool hasMipMap() const;
+    void buildMipMap(bool forceRebuild = false);
+    void freeMipMap();
+
+    /** Given scale factors sx, sy, determine the miplevel available in the
+        bitmap, and return it (this is the amount to shift matrix iterators
+        by). If dst is not null, it is set to the correct level.
+    */
+    int extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    bool hasHardwareMipMap() const {
+        return fHasHardwareMipMap;
+    }
+
+    void setHasHardwareMipMap(bool hasHardwareMipMap) {
+        fHasHardwareMipMap = hasHardwareMipMap;
+    }
+#endif
+
+    bool extractAlpha(SkBitmap* dst) const {
+        return this->extractAlpha(dst, NULL, NULL, NULL);
+    }
+
+    bool extractAlpha(SkBitmap* dst, const SkPaint* paint,
+                      SkIPoint* offset) const {
+        return this->extractAlpha(dst, paint, NULL, offset);
+    }
+
+    /** Set dst to contain alpha layer of this bitmap. If destination bitmap
+        fails to be initialized, e.g. because allocator can't allocate pixels
+        for it, dst will not be modified and false will be returned.
+
+        @param dst The bitmap to be filled with alpha layer
+        @param paint The paint to draw with
+        @param allocator Allocator used to allocate the pixelref for the dst
+                         bitmap. If this is null, the standard HeapAllocator
+                         will be used.
+        @param offset If not null, it is set to top-left coordinate to position
+                      the returned bitmap so that it visually lines up with the
+                      original
+    */
+    bool extractAlpha(SkBitmap* dst, const SkPaint* paint, Allocator* allocator,
+                      SkIPoint* offset) const;
+
+    void flatten(SkFlattenableWriteBuffer&) const;
+    void unflatten(SkFlattenableReadBuffer&);
+
+    SkDEBUGCODE(void validate() const;)
+
+    class Allocator : public SkRefCnt {
+    public:
+        /** 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
+            returns. If the config requires a colortable, it also must be
+            installed via setColorTable. If false is returned, the bitmap and
+            colortable should be left unchanged.
+        */
+        virtual bool allocPixelRef(SkBitmap*, SkColorTable*) = 0;
+    };
+
+    /** Subclass of Allocator that returns a pixelref that allocates its pixel
+        memory from the heap. This is the default Allocator invoked by
+        allocPixels().
+    */
+    class HeapAllocator : public Allocator {
+    public:
+        virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
+    };
+
+    class RLEPixels {
+    public:
+        RLEPixels(int width, int height);
+        virtual ~RLEPixels();
+
+        uint8_t* packedAtY(int y) const {
+            SkASSERT((unsigned)y < (unsigned)fHeight);
+            return fYPtrs[y];
+        }
+
+        // called by subclasses during creation
+        void setPackedAtY(int y, uint8_t* addr) {
+            SkASSERT((unsigned)y < (unsigned)fHeight);
+            fYPtrs[y] = addr;
+        }
+
+    private:
+        uint8_t** fYPtrs;
+        int       fHeight;
+    };
+
+private:
+    struct MipMap;
+    mutable MipMap* fMipMap;
+
+    mutable SkPixelRef* fPixelRef;
+    mutable size_t      fPixelRefOffset;
+    mutable int         fPixelLockCount;
+    // either user-specified (in which case it is not treated as mutable)
+    // 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,
+        kImageIsVolatile_Flag   = 0x02,
+        kImageIsImmutable_Flag  = 0x04
+    };
+
+    uint32_t    fRowBytes;
+    uint32_t    fWidth;
+    uint32_t    fHeight;
+    uint8_t     fConfig;
+    uint8_t     fFlags;
+    uint8_t     fBytesPerPixel; // based on config
+
+#ifdef SK_BUILD_FOR_ANDROID
+    bool fHasHardwareMipMap;
+#endif
+
+    /* Internal computations for safe size.
+    */
+    static Sk64 ComputeSafeSize64(Config config,
+                                  uint32_t width,
+                                  uint32_t height,
+                                  uint32_t rowBytes);
+    static size_t ComputeSafeSize(Config   config,
+                                  uint32_t width,
+                                  uint32_t height,
+                                  uint32_t rowBytes);
+
+    /*  Unreference any pixelrefs or colortables
+    */
+    void freePixels();
+    void updatePixelsFromRef() const;
+
+    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) {
+        fDidLock = doLock;
+        if (doLock) {
+            bm.lockPixels();
+        }
+    }
+    ~SkAutoLockPixels() {
+        if (fDidLock) {
+            fBitmap.unlockPixels();
+        }
+    }
+
+private:
+    const SkBitmap& fBitmap;
+    bool            fDidLock;
+};
+
+/** Helper class that performs the lock/unlockColors calls on a colortable.
+    The destructor will call unlockColors(false) if it has a bitmap's colortable
+*/
+class SkAutoLockColors : public SkNoncopyable {
+public:
+    /** Initialize with no bitmap. Call lockColors(bitmap) to lock bitmap's
+        colortable
+     */
+    SkAutoLockColors() : fCTable(NULL), fColors(NULL) {}
+    /** Initialize with bitmap, locking its colortable if present
+     */
+    explicit SkAutoLockColors(const SkBitmap& bm) {
+        fCTable = bm.getColorTable();
+        fColors = fCTable ? fCTable->lockColors() : NULL;
+    }
+    /** Initialize with a colortable (may be null)
+     */
+    explicit SkAutoLockColors(SkColorTable* ctable) {
+        fCTable = ctable;
+        fColors = ctable ? ctable->lockColors() : NULL;
+    }
+    ~SkAutoLockColors() {
+        if (fCTable) {
+            fCTable->unlockColors(false);
+        }
+    }
+
+    /** Return the currently locked colors, or NULL if no bitmap's colortable
+        is currently locked.
+    */
+    const SkPMColor* colors() const { return fColors; }
+
+    /** Locks the table and returns is colors (assuming ctable is not null) and
+        unlocks the previous table if one was present
+     */
+    const SkPMColor* lockColors(SkColorTable* ctable) {
+        if (fCTable) {
+            fCTable->unlockColors(false);
+        }
+        fCTable = ctable;
+        fColors = ctable ? ctable->lockColors() : NULL;
+        return fColors;
+    }
+
+    const SkPMColor* lockColors(const SkBitmap& bm) {
+        return this->lockColors(bm.getColorTable());
+    }
+
+private:
+    SkColorTable*    fCTable;
+    const SkPMColor* fColors;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline uint32_t* SkBitmap::getAddr32(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kARGB_8888_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint32_t*)((char*)fPixels + y * fRowBytes + (x << 2));
+}
+
+inline uint16_t* SkBitmap::getAddr16(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kRGB_565_Config || fConfig == kARGB_4444_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint16_t*)((char*)fPixels + y * fRowBytes + (x << 1));
+}
+
+inline uint8_t* SkBitmap::getAddr8(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kA8_Config || fConfig == kIndex8_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint8_t*)fPixels + y * fRowBytes + x;
+}
+
+inline SkPMColor SkBitmap::getIndex8Color(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kIndex8_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    SkASSERT(fColorTable);
+    return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)];
+}
+
+// returns the address of the byte that contains the x coordinate
+inline uint8_t* SkBitmap::getAddr1(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kA1_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint8_t*)fPixels + y * fRowBytes + (x >> 3);
+}
+
+#endif
diff --git a/legacy/include/core/SkBlitRow.h b/legacy/include/core/SkBlitRow.h
new file mode 100644
index 0000000..fb62f5a
--- /dev/null
+++ b/legacy/include/core/SkBlitRow.h
@@ -0,0 +1,94 @@
+/*
+ * 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 SkBlitRow_DEFINED
+#define SkBlitRow_DEFINED
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+
+class SkBlitRow {
+public:
+    enum Flags16 {
+        //! If set, the alpha parameter will be != 255
+        kGlobalAlpha_Flag   = 0x01,
+        //! If set, the src colors may have alpha != 255
+        kSrcPixelAlpha_Flag = 0x02,
+        //! If set, the resulting 16bit colors should be dithered
+        kDither_Flag        = 0x04
+    };
+
+    /** Function pointer that reads a scanline of src SkPMColors, and writes
+        a corresponding scanline of 16bit colors (specific format based on the
+        config passed to the Factory.
+
+        The x,y params are useful just for dithering
+
+        @param alpha A global alpha to be applied to all of the src colors
+        @param x The x coordinate of the beginning of the scanline
+        @param y THe y coordinate of the scanline
+     */
+    typedef void (*Proc)(uint16_t* dst,
+                         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,
+    };
+
+    /** Function pointer that blends 32bit colors onto a 32bit destination.
+        @param dst  array of dst 32bit colors
+        @param src  array of src 32bit colors (w/ or w/o alpha)
+        @param count number of colors to blend
+        @param alpha global alpha to be applied to all src colors
+     */
+    typedef void (*Proc32)(uint32_t* dst,
+                         const SkPMColor* src,
+                         int count, U8CPU alpha);
+
+    static Proc32 Factory32(unsigned flags32);
+
+    /** Blend a single color onto a row of S32 pixels, writing the result
+        into a row of D32 pixels. src and dst may be the same memory, but
+        if they are not, they may not overlap.
+     */
+    static void Color32(SkPMColor dst[], const SkPMColor src[],
+                        int count, SkPMColor color);
+
+    static ColorProc ColorProcFactory();
+
+    /** These static functions are called by the Factory and Factory32
+        functions, and should return either NULL, or a
+        platform-specific function-ptr to be used in place of the
+        system default.
+     */
+
+    static Proc32 PlatformProcs32(unsigned flags);
+    static Proc PlatformProcs565(unsigned flags);
+    static Proc PlatformProcs4444(unsigned flags);
+    static ColorProc PlatformColorProc();
+
+private:
+    enum {
+        kFlags16_Mask = 7,
+        kFlags32_Mask = 3
+    };
+};
+
+#endif
diff --git a/include/core/SkBlitter.h b/legacy/include/core/SkBlitter.h
similarity index 100%
rename from include/core/SkBlitter.h
rename to legacy/include/core/SkBlitter.h
diff --git a/legacy/include/core/SkBounder.h b/legacy/include/core/SkBounder.h
new file mode 100644
index 0000000..5bac358
--- /dev/null
+++ b/legacy/include/core/SkBounder.h
@@ -0,0 +1,90 @@
+
+/*
+ * 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 SkBounder_DEFINED
+#define SkBounder_DEFINED
+
+#include "SkTypes.h"
+#include "SkRefCnt.h"
+#include "SkPoint.h"
+
+struct SkGlyph;
+struct SkIRect;
+struct SkPoint;
+struct SkRect;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+/** \class SkBounder
+
+    Base class for intercepting the device bounds of shapes before they are drawn.
+    Install a subclass of this in your canvas.
+*/
+class SkBounder : public SkRefCnt {
+public:
+    SkBounder();
+
+    /* Call to perform a clip test before calling onIRect. 
+       Returns the result from onIRect.
+    */
+    bool doIRect(const SkIRect&);
+    bool doIRectGlyph(const SkIRect& , int x, int y, const SkGlyph&);
+
+protected:
+    /** Override in your subclass. This is called with the device bounds of an
+        object (text, geometry, image) just before it is drawn. If your method
+        returns false, the drawing for that shape is aborted. If your method
+        returns true, drawing continues. The bounds your method receives have already
+        been transformed in to device coordinates, and clipped to the current clip.
+    */
+    virtual bool onIRect(const SkIRect&) {
+        return false;
+    }
+
+    /** Passed to onIRectGlyph with the information about the current glyph.
+        LSB and RSB are fixed-point (16.16) coordinates of the start and end
+        of the glyph's advance
+     */
+    struct GlyphRec {
+        SkIPoint    fLSB;   //!< fixed-point left-side-bearing of the glyph
+        SkIPoint    fRSB;   //!< fixed-point right-side-bearing of the glyph
+        uint16_t    fGlyphID;
+        uint16_t    fFlags; //!< currently set to 0
+    };
+
+    /** Optionally, override in your subclass to receive the glyph ID when
+        text drawing supplies the device bounds of the object.
+    */
+    virtual bool onIRectGlyph(const SkIRect& r, const GlyphRec&) {
+        return onIRect(r);
+    }
+
+    /** Called after each shape has been drawn. The default implementation does
+        nothing, but your override could use this notification to signal itself
+        that the offscreen being rendered into needs to be updated to the screen.
+    */
+    virtual void commit();
+
+private:
+    bool doHairline(const SkPoint&, const SkPoint&, const SkPaint&);
+    bool doRect(const SkRect&, const SkPaint&);
+    bool doPath(const SkPath&, const SkPaint&, bool doFill);
+    void setClip(const SkRegion* clip) { fClip = clip; }
+
+    const SkRegion* fClip;
+    friend class SkAutoBounderCommit;
+    friend class SkDraw;
+    friend class SkDrawIter;
+    friend struct Draw1Glyph;
+    friend class SkMaskFilter;
+};
+
+#endif
+
diff --git a/include/core/SkBuffer.h b/legacy/include/core/SkBuffer.h
similarity index 100%
rename from include/core/SkBuffer.h
rename to legacy/include/core/SkBuffer.h
diff --git a/legacy/include/core/SkCanvas.h b/legacy/include/core/SkCanvas.h
new file mode 100644
index 0000000..1b9f055
--- /dev/null
+++ b/legacy/include/core/SkCanvas.h
@@ -0,0 +1,1076 @@
+
+/*
+ * 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 SkCanvas_DEFINED
+#define SkCanvas_DEFINED
+
+#include "SkTypes.h"
+#include "SkBitmap.h"
+#include "SkDeque.h"
+#include "SkClipStack.h"
+#include "SkPaint.h"
+#include "SkRefCnt.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkScalarCompare.h"
+#include "SkXfermode.h"
+
+class SkBounder;
+class SkDevice;
+class SkDraw;
+class SkDrawFilter;
+class SkPicture;
+
+/** \class SkCanvas
+
+    A Canvas encapsulates all of the state about drawing into a device (bitmap).
+    This includes a reference to the device itself, and a stack of matrix/clip
+    values. For any given draw call (e.g. drawRect), the geometry of the object
+    being drawn is transformed by the concatenation of all the matrices in the
+    stack. The transformed geometry is clipped by the intersection of all of
+    the clips in the stack.
+
+    While the Canvas holds the state of the drawing device, the state (style)
+    of the object being drawn is held by the Paint, which is provided as a
+    parameter to each of the draw() methods. The Paint holds attributes such as
+    color, typeface, textSize, strokeWidth, shader (e.g. gradients, patterns),
+    etc.
+*/
+class SK_API SkCanvas : public SkRefCnt {
+public:
+    SkCanvas();
+
+    /** Construct a canvas with the specified device to draw into.
+
+        @param device   Specifies a device for the canvas to draw into.
+    */
+    explicit SkCanvas(SkDevice* device);
+
+    /** Deprecated - Construct a canvas with the specified bitmap to draw into.
+        @param bitmap   Specifies a bitmap for the canvas to draw into. Its
+                        structure are copied to the canvas.
+    */
+    explicit SkCanvas(const SkBitmap& bitmap);
+    virtual ~SkCanvas();
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     *  Trigger the immediate execution of all pending draw operations.
+     */
+    void flush();
+
+    /**
+     *  Return the width/height of the underlying device. The current drawable
+     *  area may be small (due to clipping or saveLayer). For a canvas with
+     *  no device, 0,0 will be returned.
+     */
+    SkISize getDeviceSize() const;
+
+    /** Return the canvas' device object, which may be null. The device holds
+        the bitmap of the pixels that the canvas draws into. The reference count
+        of the returned device is not changed by this call.
+    */
+    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.
+     */
+    SkDevice* getTopDevice() const;
+
+    /**
+     *  Create a new raster device and make it current. This also returns
+     *  the new device.
+     */
+    SkDevice* setBitmapDevice(const SkBitmap& bitmap);
+
+    /**
+     *  Shortcut for getDevice()->createCompatibleDevice(...).
+     *  If getDevice() == NULL, this method does nothing, and returns NULL.
+     */
+    SkDevice* createCompatibleDevice(SkBitmap::Config config,
+                                    int width, int height,
+                                    bool isOpaque);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * This enum can be used with read/writePixels to perform a pixel ops to or
+     * from an 8888 config other than Skia's native config (SkPMColor). There
+     * are three byte orders supported: native, BGRA, and RGBA. Each has a
+     * premultiplied and unpremultiplied variant.
+     *
+     * Components of a 8888 pixel can be packed/unpacked from a 32bit word using
+     * either byte offsets or shift values. Byte offsets are endian-invariant
+     * while shifts are not. BGRA and RGBA configs are defined by byte
+     * orderings. The native config is defined by shift values (SK_A32_SHIFT,
+     * ..., SK_B32_SHIFT).
+     */
+    enum Config8888 {
+        /**
+         * Skia's native order specified by:
+         *      SK_A32_SHIFT, SK_R32_SHIFT, SK_G32_SHIFT, and SK_B32_SHIFT
+         *
+         * kNative_Premul_Config8888 is equivalent to SkPMColor
+         * kNative_Unpremul_Config8888 has the same component order as SkPMColor
+         * but is not premultiplied.
+         */
+        kNative_Premul_Config8888,
+        kNative_Unpremul_Config8888,
+        /**
+         * low byte to high byte: B, G, R, A.
+         */
+        kBGRA_Premul_Config8888,
+        kBGRA_Unpremul_Config8888,
+        /**
+         * low byte to high byte: R, G, B, A.
+         */
+        kRGBA_Premul_Config8888,
+        kRGBA_Unpremul_Config8888,
+    };
+
+    /**
+     *  On success (returns true), copy the canvas pixels into the bitmap.
+     *  On failure, the bitmap parameter is left unchanged and false is
+     *  returned.
+     *
+     *  The canvas' pixels are converted to the bitmap's config. The only
+     *  supported config is kARGB_8888_Config, though this is likely to be
+     *  relaxed in  the future. The meaning of config kARGB_8888_Config is
+     *  modified by the enum param config8888. The default value interprets
+     *  kARGB_8888_Config as SkPMColor
+     *
+     *  If the bitmap has pixels already allocated, the canvas pixels will be
+     *  written there. If not, bitmap->allocPixels() will be called
+     *  automatically. If the bitmap is backed by a texture readPixels will
+     *  fail.
+     *
+     *  The actual pixels written is the intersection of the canvas' bounds, and
+     *  the rectangle formed by the bitmap's width,height and the specified x,y.
+     *  If bitmap pixels extend outside of that intersection, they will not be
+     *  modified.
+     *
+     *  Other failure conditions:
+     *    * If the canvas is backed by a non-raster device (e.g. PDF) then
+     *       readPixels will fail.
+     *    * If bitmap is texture-backed then readPixels will fail. (This may be
+     *       relaxed in the future.)
+     *
+     *  Example that reads the entire canvas into a bitmap using the native
+     *  SkPMColor:
+     *    SkISize size = canvas->getDeviceSize();
+     *    bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
+     *                                                   size.fHeight);
+     *    if (canvas->readPixels(bitmap, 0, 0)) {
+     *       // use the pixels
+     *    }
+     */
+    bool readPixels(SkBitmap* bitmap,
+                    int x, int y,
+                    Config8888 config8888 = kNative_Premul_Config8888);
+
+    /**
+     * DEPRECATED: This will be removed as soon as webkit is no longer relying
+     * on it. The bitmap is resized to the intersection of srcRect and the
+     * canvas bounds. New pixels are always allocated on success. Bitmap is
+     * unmodified on failure.
+     */
+    bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap);
+
+    /**
+     *  Similar to draw sprite, this method will copy the pixels in bitmap onto
+     *  the canvas, with the top/left corner specified by (x, y). The canvas'
+     *  pixel values are completely replaced: there is no blending.
+     *
+     *  Currently if bitmap is backed by a texture this is a no-op. This may be
+     *  relaxed in the future.
+     *
+     *  If the bitmap has config kARGB_8888_Config then the config8888 param
+     *  will determines how the pixel valuess are intepreted. If the bitmap is
+     *  not kARGB_8888_Config then this parameter is ignored.
+     *
+     *  Note: If you are recording drawing commands on this canvas to
+     *  SkPicture, writePixels() is ignored!
+     */
+    void writePixels(const SkBitmap& bitmap,
+                     int x, int y,
+                     Config8888 config8888 = kNative_Premul_Config8888);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    enum SaveFlags {
+        /** save the matrix state, restoring it on restore() */
+        kMatrix_SaveFlag            = 0x01,
+        /** save the clip state, restoring it on restore() */
+        kClip_SaveFlag              = 0x02,
+        /** the layer needs to support per-pixel alpha */
+        kHasAlphaLayer_SaveFlag     = 0x04,
+        /** the layer needs to support 8-bits per color component */
+        kFullColorLayer_SaveFlag    = 0x08,
+        /** the layer should clip against the bounds argument */
+        kClipToLayer_SaveFlag       = 0x10,
+
+        // helper masks for common choices
+        kMatrixClip_SaveFlag        = 0x03,
+        kARGB_NoClipLayer_SaveFlag  = 0x0F,
+        kARGB_ClipLayer_SaveFlag    = 0x1F
+    };
+
+    /** This call saves the current matrix, clip, and drawFilter, and pushes a
+        copy onto a private stack. Subsequent calls to translate, scale,
+        rotate, skew, concat or clipRect, clipPath, and setDrawFilter all
+        operate on this copy.
+        When the balancing call to restore() is made, the previous matrix, clip,
+        and drawFilter are restored.
+        @return The value to pass to restoreToCount() to balance this save()
+    */
+    virtual int save(SaveFlags flags = kMatrixClip_SaveFlag);
+
+    /** This behaves the same as save(), but in addition it allocates an
+        offscreen bitmap. All drawing calls are directed there, and only when
+        the balancing call to restore() is made is that offscreen transfered to
+        the canvas (or the previous layer).
+        @param bounds (may be null) This rect, if non-null, is used as a hint to
+                      limit the size of the offscreen, and thus drawing may be
+                      clipped to it, though that clipping is not guaranteed to
+                      happen. If exact clipping is desired, use clipRect().
+        @param paint (may be null) This is copied, and is applied to the
+                     offscreen when restore() is called
+        @param flags  LayerFlags
+        @return The value to pass to restoreToCount() to balance this save()
+    */
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags = kARGB_ClipLayer_SaveFlag);
+
+    /** This behaves the same as save(), but in addition it allocates an
+        offscreen bitmap. All drawing calls are directed there, and only when
+        the balancing call to restore() is made is that offscreen transfered to
+        the canvas (or the previous layer).
+        @param bounds (may be null) This rect, if non-null, is used as a hint to
+                      limit the size of the offscreen, and thus drawing may be
+                      clipped to it, though that clipping is not guaranteed to
+                      happen. If exact clipping is desired, use clipRect().
+        @param alpha  This is applied to the offscreen when restore() is called.
+        @param flags  LayerFlags
+        @return The value to pass to restoreToCount() to balance this save()
+    */
+    int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+                       SaveFlags flags = kARGB_ClipLayer_SaveFlag);
+
+    /** This call balances a previous call to save(), and is used to remove all
+        modifications to the matrix/clip/drawFilter state since the last save
+        call.
+        It is an error to call restore() more times than save() was called.
+    */
+    virtual void restore();
+
+    /** Returns the number of matrix/clip states on the SkCanvas' private stack.
+        This will equal # save() calls - # restore() calls.
+    */
+    int getSaveCount() const;
+
+    /** Efficient way to pop any calls to save() that happened after the save
+        count reached saveCount. It is an error for saveCount to be less than
+        getSaveCount()
+        @param saveCount    The number of save() levels to restore from
+    */
+    void restoreToCount(int saveCount);
+
+    /** Returns true if drawing is currently going to a layer (from saveLayer)
+     *  rather than to the root device.
+     */
+    virtual bool isDrawingToLayer() const;
+
+    /** Preconcat the current matrix with the specified translation
+        @param dx   The distance to translate in X
+        @param dy   The distance to translate in Y
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool translate(SkScalar dx, SkScalar dy);
+
+    /** Preconcat the current matrix with the specified scale.
+        @param sx   The amount to scale in X
+        @param sy   The amount to scale in Y
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool scale(SkScalar sx, SkScalar sy);
+
+    /** Preconcat the current matrix with the specified rotation.
+        @param degrees  The amount to rotate, in degrees
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool rotate(SkScalar degrees);
+
+    /** Preconcat the current matrix with the specified skew.
+        @param sx   The amount to skew in X
+        @param sy   The amount to skew in Y
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool skew(SkScalar sx, SkScalar sy);
+
+    /** Preconcat the current matrix with the specified matrix.
+        @param matrix   The matrix to preconcatenate with the current matrix
+        @return true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool concat(const SkMatrix& matrix);
+
+    /** Replace the current matrix with a copy of the specified matrix.
+        @param matrix The matrix that will be copied into the current matrix.
+    */
+    virtual void setMatrix(const SkMatrix& matrix);
+
+    /** Helper for setMatrix(identity). Sets the current matrix to identity.
+    */
+    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
+    */
+    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
+    */
+    virtual bool clipPath(const SkPath& path,
+                          SkRegion::Op op = SkRegion::kIntersect_Op,
+                          bool doAntiAlias = false);
+
+    /** 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
+        coordinates, and so no transformation is performed.
+        @param deviceRgn    The region 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
+    */
+    virtual bool clipRegion(const SkRegion& deviceRgn,
+                            SkRegion::Op op = SkRegion::kIntersect_Op);
+
+    /** Helper for clipRegion(rgn, kReplace_Op). Sets the current clip to the
+        specified region. This does not intersect or in any other way account
+        for the existing clip region.
+        @param deviceRgn The region to copy into the current clip.
+        @return true if the new clip region is non-empty
+    */
+    bool setClipRegion(const SkRegion& deviceRgn) {
+        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;
+
+    /** Return true if the specified path, 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). Note, for speed it may
+        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;
+
+    /** Return true if the horizontal band specified by top and bottom is
+        completely clipped out. This is a conservative calculation, meaning
+        that it is possible that if the method returns false, the band may still
+        in fact be clipped out, but the converse is not true. If this method
+        returns true, then the band is guaranteed to be clipped out.
+        @param top  The top of the horizontal band to compare with the clip
+        @param bottom The bottom of the horizontal and to compare with the clip
+        @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 {
+        SkASSERT(SkScalarToCompareType(top) <= SkScalarToCompareType(bottom));
+        const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
+        // 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
+        // check as it is rare and would result double the comparisons.
+        return SkScalarToCompareType(top) >= clipR.fBottom
+            || SkScalarToCompareType(bottom) <= clipR.fTop;
+    }
+
+    /** Return the bounds of the current clip (in local coordinates) in the
+        bounds parameter, and return true if it is non-empty. This can be useful
+        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;
+
+    /** Return the bounds of the current clip, in device coordinates; returns
+        true if non-empty. Maybe faster than getting the clip explicitly and
+        then taking its bounds.
+    */
+    bool getClipDeviceBounds(SkIRect* bounds) const;
+
+
+    /** Fill the entire canvas' bitmap (restricted to the current clip) with the
+        specified ARGB color, using the specified mode.
+        @param a    the alpha component (0..255) of the color to fill the canvas
+        @param r    the red component (0..255) of the color to fill the canvas
+        @param g    the green component (0..255) of the color to fill the canvas
+        @param b    the blue component (0..255) of the color to fill the canvas
+        @param mode the mode to apply the color in (defaults to SrcOver)
+    */
+    void drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+                  SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode);
+
+    /** Fill the entire canvas' bitmap (restricted to the current clip) with the
+        specified color and mode.
+        @param color    the color to draw with
+        @param mode the mode to apply the color in (defaults to SrcOver)
+    */
+    void drawColor(SkColor color,
+                   SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode);
+
+    /**
+     *  This erases the entire drawing surface to the specified color,
+     *  irrespective of the clip. It does not blend with the previous pixels,
+     *  but always overwrites them.
+     *
+     *  It is roughly equivalent to the following:
+     *      canvas.save();
+     *      canvas.clipRect(hugeRect, kReplace_Op);
+     *      paint.setColor(color);
+     *      paint.setXfermodeMode(kSrc_Mode);
+     *      canvas.drawPaint(paint);
+     *      canvas.restore();
+     *  though it is almost always much more efficient.
+     */
+    virtual void clear(SkColor);
+
+    /**
+     *  Fill the entire canvas' bitmap (restricted to the current clip) with the
+     *  specified paint.
+     *  @param paint    The paint used to fill the canvas
+     */
+    virtual void drawPaint(const SkPaint& paint);
+
+    enum PointMode {
+        /** drawPoints draws each point separately */
+        kPoints_PointMode,
+        /** drawPoints draws each pair of points as a line segment */
+        kLines_PointMode,
+        /** drawPoints draws the array of points as a polygon */
+        kPolygon_PointMode
+    };
+
+    /** Draw a series of points, interpreted based on the PointMode mode. For
+        all modes, the count parameter is interpreted as the total number of
+        points. For kLine mode, count/2 line segments are drawn.
+        For kPoint mode, each point is drawn centered at its coordinate, and its
+        size is specified by the paint's stroke-width. It draws as a square,
+        unless the paint's cap-type is round, in which the points are drawn as
+        circles.
+        For kLine mode, each pair of points is drawn as a line segment,
+        respecting the paint's settings for cap/join/width.
+        For kPolygon mode, the entire array is drawn as a series of connected
+        line segments.
+        Note that, while similar, kLine and kPolygon modes draw slightly
+        differently than the equivalent path built with a series of moveto,
+        lineto calls, in that the path will draw all of its contours at once,
+        with no interactions if contours intersect each other (think XOR
+        xfermode). drawPoints always draws each element one at a time.
+        @param mode     PointMode specifying how to draw the array of points.
+        @param count    The number of points in the array
+        @param pts      Array of points to draw
+        @param paint    The paint used to draw the points
+    */
+    virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                            const SkPaint& paint);
+
+    /** Helper method for drawing a single point. See drawPoints() for a more
+        details.
+    */
+    void drawPoint(SkScalar x, SkScalar y, const SkPaint& paint);
+
+    /** Draws a single pixel in the specified color.
+        @param x        The X coordinate of which pixel to draw
+        @param y        The Y coordiante of which pixel to draw
+        @param color    The color to draw
+    */
+    void drawPoint(SkScalar x, SkScalar y, SkColor color);
+
+    /** Draw a line segment with the specified start and stop x,y coordinates,
+        using the specified paint. NOTE: since a line is always "framed", the
+        paint's Style is ignored.
+        @param x0    The x-coordinate of the start point of the line
+        @param y0    The y-coordinate of the start point of the line
+        @param x1    The x-coordinate of the end point of the line
+        @param y1    The y-coordinate of the end point of the line
+        @param paint The paint used to draw the line
+    */
+    void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
+                  const SkPaint& paint);
+
+    /** Draw the specified rectangle using the specified paint. The rectangle
+        will be filled or stroked based on the Style in the paint.
+        @param rect     The rect to be drawn
+        @param paint    The paint used to draw the rect
+    */
+    virtual void drawRect(const SkRect& rect, const SkPaint& paint);
+
+    /** Draw the specified rectangle using the specified paint. The rectangle
+        will be filled or framed based on the Style in the paint.
+        @param rect     The rect to be drawn
+        @param paint    The paint used to draw the rect
+    */
+    void drawIRect(const SkIRect& rect, const SkPaint& paint)
+    {
+        SkRect r;
+        r.set(rect);    // promotes the ints to scalars
+        this->drawRect(r, paint);
+    }
+
+    /** Draw the specified rectangle using the specified paint. The rectangle
+        will be filled or framed based on the Style in the paint.
+        @param left     The left side of the rectangle to be drawn
+        @param top      The top side of the rectangle to be drawn
+        @param right    The right side of the rectangle to be drawn
+        @param bottom   The bottom side of the rectangle to be drawn
+        @param paint    The paint used to draw the rect
+    */
+    void drawRectCoords(SkScalar left, SkScalar top, SkScalar right,
+                        SkScalar bottom, const SkPaint& paint);
+
+    /** Draw the specified oval using the specified paint. The oval will be
+        filled or framed based on the Style in the paint.
+        @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&);
+
+    /** Draw the specified circle using the specified paint. If radius is <= 0,
+        then nothing will be drawn. The circle will be filled
+        or framed based on the Style in the paint.
+        @param cx       The x-coordinate of the center of the cirle to be drawn
+        @param cy       The y-coordinate of the center of the cirle to be drawn
+        @param radius   The radius of the cirle to be drawn
+        @param paint    The paint used to draw the circle
+    */
+    void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
+                    const SkPaint& paint);
+
+    /** Draw the specified arc, which will be scaled to fit inside the
+        specified oval. If the sweep angle is >= 360, then the oval is drawn
+        completely. Note that this differs slightly from SkPath::arcTo, which
+        treats the sweep angle mod 360.
+        @param oval The bounds of oval used to define the shape of the arc
+        @param startAngle Starting angle (in degrees) where the arc begins
+        @param sweepAngle Sweep angle (in degrees) measured clockwise
+        @param useCenter true means include the center of the oval. For filling
+                         this will draw a wedge. False means just use the arc.
+        @param paint    The paint used to draw the arc
+    */
+    void drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+                 bool useCenter, const SkPaint& paint);
+
+    /** Draw the specified round-rect using the specified paint. The round-rect
+        will be filled or framed based on the Style in the paint.
+        @param rect     The rectangular bounds of the roundRect to be drawn
+        @param rx       The x-radius of the oval used to round the corners
+        @param ry       The y-radius of the oval used to round the corners
+        @param paint    The paint used to draw the roundRect
+    */
+    void drawRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
+                       const SkPaint& paint);
+
+    /** Draw the specified path using the specified paint. The path will be
+        filled or framed based on the Style in the paint.
+        @param path     The path to be drawn
+        @param paint    The paint used to draw the path
+    */
+    virtual void drawPath(const SkPath& path, const SkPaint& paint);
+
+    /** Draw the specified bitmap, with its top/left corner at (x,y), using the
+        specified paint, transformed by the current matrix. Note: if the paint
+        contains a maskfilter that generates a mask which extends beyond the
+        bitmap's original width/height, then the bitmap will be drawn as if it
+        were in a Shader with CLAMP mode. Thus the color outside of the original
+        width/height will be the edge color replicated.
+        @param bitmap   The bitmap to be drawn
+        @param left     The position of the left side of the bitmap being drawn
+        @param top      The position of the top side of the bitmap being drawn
+        @param paint    The paint used to draw the bitmap, or NULL
+    */
+    virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                            const SkPaint* paint = NULL);
+
+    /** Draw the specified bitmap, with the specified matrix applied (before the
+        canvas' matrix is applied).
+        @param bitmap   The bitmap to be drawn
+        @param src      Optional: specify the subset of the bitmap to be drawn
+        @param dst      The destination rectangle where the scaled/translated
+                        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 drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint* paint = NULL);
+
+    /**
+     *  Draw the bitmap stretched differentially to fit into dst.
+     *  center is a rect within the bitmap, and logically divides the bitmap
+     *  into 9 sections (3x3). For example, if the middle pixel of a [5x5]
+     *  bitmap is the "center", then the center-rect should be [2, 2, 3, 3].
+     *
+     *  If the dst is >= the bitmap size, then...
+     *  - The 4 corners are not stretch at all.
+     *  - The sides are stretch in only one axis.
+     *  - The center is stretch in both axes.
+     * Else, for each axis where dst < bitmap,
+     *  - The corners shrink proportionally
+     *  - The sides (along the shrink axis) and center are not drawn
+     */
+    virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                const SkRect& dst, const SkPaint* paint = NULL);
+
+    /** Draw the specified bitmap, with its top/left corner at (x,y),
+        NOT transformed by the current matrix. Note: if the paint
+        contains a maskfilter that generates a mask which extends beyond the
+        bitmap's original width/height, then the bitmap will be drawn as if it
+        were in a Shader with CLAMP mode. Thus the color outside of the original
+        width/height will be the edge color replicated.
+        @param bitmap   The bitmap to be drawn
+        @param left     The position of the left side of the bitmap being drawn
+        @param top      The position of the top side of the bitmap being drawn
+        @param paint    The paint used to draw the bitmap, or NULL
+    */
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint = NULL);
+
+    /** Draw the text, with origin at (x,y), using the specified paint.
+        The origin is interpreted based on the Align setting in the paint.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param x        The x-coordinate of the origin of the text being drawn
+        @param y        The y-coordinate of the origin of the text being drawn
+        @param paint    The paint used for the text (e.g. color, size, style)
+    */
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, const SkPaint& paint);
+
+    /** Draw the text, with each character/glyph origin specified by the pos[]
+        array. The origin is interpreted by the Align setting in the paint.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param pos      Array of positions, used to position each character
+        @param paint    The paint used for the text (e.g. color, size, style)
+        */
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint& paint);
+
+    /** Draw the text, with each character/glyph origin specified by the x
+        coordinate taken from the xpos[] array, and the y from the constY param.
+        The origin is interpreted by the Align setting in the paint.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param xpos     Array of x-positions, used to position each character
+        @param constY   The shared Y coordinate for all of the positions
+        @param paint    The paint used for the text (e.g. color, size, style)
+        */
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint& paint);
+
+    /** Draw the text, with origin at (x,y), using the specified paint, along
+        the specified path. The paint's Align setting determins where along the
+        path to start the text.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param path         The path the text should follow for its baseline
+        @param hOffset      The distance along the path to add to the text's
+                            starting position
+        @param vOffset      The distance above(-) or below(+) the path to
+                            position the text
+        @param paint        The paint used for the text
+    */
+    void drawTextOnPathHV(const void* text, size_t byteLength,
+                          const SkPath& path, SkScalar hOffset,
+                          SkScalar vOffset, const SkPaint& paint);
+
+    /** Draw the text, with origin at (x,y), using the specified paint, along
+        the specified path. The paint's Align setting determins where along the
+        path to start the text.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param path         The path the text should follow for its baseline
+        @param matrix       (may be null) Applied to the text before it is
+                            mapped onto the path
+        @param paint        The paint used for the text
+        */
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    /** Draw the text on path, with each character/glyph origin specified by the pos[]
+        array. The origin is interpreted by the Align setting in the paint.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param pos      Array of positions, used to position each character
+        @param paint    The paint used for the text (e.g. color, size, style)
+        @param path The path to draw on
+        @param matrix The canvas matrix
+        */
+    void drawPosTextOnPath(const void* text, size_t byteLength,
+                           const SkPoint pos[], const SkPaint& paint,
+                           const SkPath& path, const SkMatrix* matrix);
+#endif
+
+    /** 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.
+        @param picture The recorded drawing commands to playback into this
+                       canvas.
+    */
+    virtual void drawPicture(SkPicture& picture);
+
+    enum VertexMode {
+        kTriangles_VertexMode,
+        kTriangleStrip_VertexMode,
+        kTriangleFan_VertexMode
+    };
+
+    /** Draw the array of vertices, interpreted as triangles (based on mode).
+        @param vmode How to interpret the array of vertices
+        @param vertexCount The number of points in the vertices array (and
+                    corresponding texs and colors arrays if non-null)
+        @param vertices Array of vertices for the mesh
+        @param texs May be null. If not null, specifies the coordinate
+                             in texture space for each vertex.
+        @param colors May be null. If not null, specifies a color for each
+                      vertex, to be interpolated across the triangle.
+        @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.
+        @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)
+        @param paint Specifies the shader/texture if present.
+    */
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint);
+
+    /** Send a blob of data to the canvas.
+        For canvases that draw, this call is effectively a no-op, as the data
+        is not parsed, but just ignored. However, this call exists for
+        subclasses like SkPicture's recording canvas, that can store the data
+        and then play it back later (via another call to drawData).
+     */
+    virtual void drawData(const void* data, size_t length);
+
+    //////////////////////////////////////////////////////////////////////////
+
+    /** Get the current bounder object.
+        The bounder's reference count is unchaged.
+        @return the canva's bounder (or NULL).
+    */
+    SkBounder*  getBounder() const { return fBounder; }
+
+    /** Set a new bounder (or NULL).
+        Pass NULL to clear any previous bounder.
+        As a convenience, the parameter passed is also returned.
+        If a previous bounder exists, its reference count is decremented.
+        If bounder is not NULL, its reference count is incremented.
+        @param bounder the new bounder (or NULL) to be installed in the canvas
+        @return the set bounder object
+    */
+    virtual SkBounder* setBounder(SkBounder* bounder);
+
+    /** Get the current filter object. The filter's reference count is not
+        affected. The filter is saved/restored, just like the matrix and clip.
+        @return the canvas' filter (or NULL).
+    */
+    SkDrawFilter* getDrawFilter() const;
+
+    /** Set the new filter (or NULL). Pass NULL to clear any existing filter.
+        As a convenience, the parameter is returned. If an existing filter
+        exists, its refcnt is decrement. If the new filter is not null, its
+        refcnt is incremented. The filter is saved/restored, just like the
+        matrix and clip.
+        @param filter the new filter (or NULL)
+        @return the new filter
+    */
+    virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter);
+
+    //////////////////////////////////////////////////////////////////////////
+
+    /** Return the current matrix on the canvas.
+        This does not account for the translate in any of the devices.
+        @return The current matrix on the canvas.
+    */
+    const SkMatrix& getTotalMatrix() const;
+
+    enum ClipType {
+        kEmpty_ClipType = 0,
+        kRect_ClipType,
+        kComplex_ClipType
+    };
+
+    /** Returns a description of the total clip; may be cheaper than
+        getting the clip and querying it directly.
+    */
+    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).
+    */
+    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.
+     */
+    bool getTotalClipBounds(SkIRect* bounds) const;
+
+    /**
+     *  Return the current clipstack. This mirrors the result in getTotalClip()
+     *  but is represented as a stack of geometric clips + region-ops.
+     */
+    const SkClipStack& getTotalClipStack() const;
+
+    void setExternalMatrix(const SkMatrix* = NULL);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** After calling saveLayer(), there can be any number of devices that make
+        up the top-most drawing area. LayerIter can be used to iterate through
+        those devices. Note that the iterator is only valid until the next API
+        call made on the canvas. Ownership of all pointers in the iterator stays
+        with the canvas, so none of them should be modified or deleted.
+    */
+    class SK_API LayerIter /*: SkNoncopyable*/ {
+    public:
+        /** Initialize iterator with canvas, and set values for 1st device */
+        LayerIter(SkCanvas*, bool skipEmptyClips);
+        ~LayerIter();
+
+        /** Return true if the iterator is done */
+        bool done() const { return fDone; }
+        /** Cycle to the next device */
+        void next();
+
+        // These reflect the current device in the iterator
+
+        SkDevice*       device() const;
+        const SkMatrix& matrix() const;
+        const SkRegion& clip() const;
+        const SkPaint&  paint() const;
+        int             x() const;
+        int             y() const;
+
+    private:
+        // used to embed the SkDrawIter object directly in our instance, w/o
+        // having to expose that class def to the public. There is an assert
+        // in our constructor to ensure that fStorage is large enough
+        // (though needs to be a compile-time-assert!). We use intptr_t to work
+        // safely with 32 and 64 bit machines (to ensure the storage is enough)
+        intptr_t          fStorage[32];
+        class SkDrawIter* fImpl;    // this points at fStorage
+        SkPaint           fDefaultPaint;
+        bool              fDone;
+    };
+
+protected:
+    // Returns the canvas to be used by DrawIter. Default implementation
+    // returns this. Subclasses that encapsulate an indirect canvas may
+    // need to overload this method. The impl must keep track of this, as it
+    // is not released or deleted by the caller.
+    virtual SkCanvas* canvasForDrawIter();
+
+    // all of the drawBitmap variants call this guy
+    virtual 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);
+
+private:
+    class MCRec;
+
+    SkClipStack fClipStack;
+    SkDeque     fMCStack;
+    // points to top of stack
+    MCRec*      fMCRec;
+    // the first N recs that can fit here mean we won't call malloc
+    uint32_t    fMCRecStorage[32];
+
+    SkBounder*  fBounder;
+    SkDevice*   fLastDeviceToGainFocus;
+    int         fLayerCount;    // number of successful saveLayer calls
+
+    void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&,
+                              const SkClipStack& clipStack);
+
+    bool fDeviceCMDirty;            // cleared by updateDeviceCMCache()
+    void updateDeviceCMCache();
+
+    friend class SkDrawIter;    // needs setupDrawForLayerDevice()
+
+    SkDevice* createLayerDevice(SkBitmap::Config, int width, int height,
+                                bool isOpaque);
+
+    SkDevice* init(SkDevice*);
+
+    // internal methods are not virtual, so they can safely be called by other
+    // 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,
+                                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);
+
+
+    void drawDevice(SkDevice*, int x, int y, const SkPaint*);
+    // shared by save() and saveLayer()
+    int internalSave(SaveFlags flags);
+    void internalRestore();
+    static void DrawRect(const SkDraw& draw, const SkPaint& paint,
+                         const SkRect& r, SkScalar textSize);
+    static void DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
+                                    const char text[], size_t byteLength,
+                                    SkScalar x, SkScalar y);
+
+    /*  These maintain a cache of the clip bounds in local coordinates,
+        (converted to 2s-compliment if floats are slow).
+     */
+    mutable SkRectCompareType fLocalBoundsCompareType;
+    mutable bool              fLocalBoundsCompareTypeDirty;
+
+    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;
+        }
+    }
+    void computeLocalClipBoundsCompareType(EdgeType et) const;
+
+    SkMatrix    fExternalMatrix, fExternalInverse;
+    bool        fUseExternalMatrix;
+
+    class AutoValidateClip : ::SkNoncopyable {
+    public:
+        explicit AutoValidateClip(SkCanvas* canvas) : fCanvas(canvas) {
+            fCanvas->validateClip();
+        }
+        ~AutoValidateClip() { fCanvas->validateClip(); }
+
+    private:
+        const SkCanvas* fCanvas;
+    };
+
+#ifdef SK_DEBUG
+    void validateClip() const;
+#else
+    void validateClip() const {}
+#endif
+};
+
+/** Stack helper class to automatically call restoreToCount() on the canvas
+    when this object goes out of scope. Use this to guarantee that the canvas
+    is restored to a known state.
+*/
+class SkAutoCanvasRestore : SkNoncopyable {
+public:
+    SkAutoCanvasRestore(SkCanvas* canvas, bool doSave) : fCanvas(canvas) {
+        SkASSERT(canvas);
+        fSaveCount = canvas->getSaveCount();
+        if (doSave) {
+            canvas->save();
+        }
+    }
+    ~SkAutoCanvasRestore() {
+        fCanvas->restoreToCount(fSaveCount);
+    }
+
+private:
+    SkCanvas*   fCanvas;
+    int         fSaveCount;
+};
+
+#endif
diff --git a/legacy/include/core/SkChunkAlloc.h b/legacy/include/core/SkChunkAlloc.h
new file mode 100644
index 0000000..28d3c7e
--- /dev/null
+++ b/legacy/include/core/SkChunkAlloc.h
@@ -0,0 +1,69 @@
+
+/*
+ * 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 SkChunkAlloc_DEFINED
+#define SkChunkAlloc_DEFINED
+
+#include "SkTypes.h"
+
+class SkChunkAlloc : SkNoncopyable {
+public:
+    SkChunkAlloc(size_t minSize);
+    ~SkChunkAlloc();
+
+    /** 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
+        the previous allocation may be reused, but the implementation is free
+        to ignore this call (and return 0).
+     */
+    size_t unalloc(void* ptr);
+    
+    size_t totalCapacity() const { return fTotalCapacity; }
+
+    /**
+     *  Returns true if the specified address is within one of the chunks, and
+     *  has at least 1-byte following the address (i.e. if addr points to the
+     *  end of a chunk, then contains() will return false).
+     */
+    bool contains(const void* addr) const;
+
+private:
+    struct Block;
+    Block*  fBlock;
+    size_t  fMinSize;
+    Block*  fPool;
+    size_t  fTotalCapacity;
+
+    Block* newBlock(size_t bytes, AllocFailType ftype);
+};
+
+#endif
diff --git a/include/core/SkClampRange.h b/legacy/include/core/SkClampRange.h
similarity index 100%
rename from include/core/SkClampRange.h
rename to legacy/include/core/SkClampRange.h
diff --git a/legacy/include/core/SkClipStack.h b/legacy/include/core/SkClipStack.h
new file mode 100644
index 0000000..fc96f03
--- /dev/null
+++ b/legacy/include/core/SkClipStack.h
@@ -0,0 +1,91 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkClipStack_DEFINED
+#define SkClipStack_DEFINED
+
+#include "SkDeque.h"
+#include "SkRegion.h"
+
+struct SkRect;
+class SkPath;
+
+class SK_API SkClipStack {
+public:
+    SkClipStack();
+    SkClipStack(const SkClipStack& b);
+    ~SkClipStack() {}
+
+    SkClipStack& operator=(const SkClipStack& b);
+    bool operator==(const SkClipStack& b) const;
+    bool operator!=(const SkClipStack& b) const { return !(*this == b); }
+
+    void reset();
+
+    int getSaveCount() const { return fSaveCount; }
+    void save();
+    void restore();
+
+    void clipDevRect(const SkIRect& ir,
+                     SkRegion::Op op = SkRegion::kIntersect_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);
+
+    class B2FIter {
+    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;
+        };
+
+        /**
+         *  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
+         */
+        const Clip* next();
+
+        /**
+         * Restarts the iterator on a clip stack.
+         */
+        void reset(const SkClipStack& stack);
+
+    private:
+        Clip             fClip;
+        SkDeque::F2BIter fIter;
+    };
+
+private:
+    friend class B2FIter;
+    struct Rec;
+
+    SkDeque fDeque;
+    int     fSaveCount;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkColor.h b/legacy/include/core/SkColor.h
new file mode 100644
index 0000000..e6d4352
--- /dev/null
+++ b/legacy/include/core/SkColor.h
@@ -0,0 +1,165 @@
+
+/*
+ * 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 SkColor_DEFINED
+#define SkColor_DEFINED
+
+#include "SkScalar.h"
+
+/** \file SkColor.h
+
+    Types and macros for colors
+*/
+
+/** 8-bit type for an alpha value. 0xFF is 100% opaque, 0x00 is 100% transparent.
+*/
+typedef uint8_t SkAlpha;
+/** 32 bit ARGB color value, not premultiplied. The color components are always in
+    a known order. This is different from SkPMColor, which has its bytes in a configuration
+    dependent order, to match the format of kARGB32 bitmaps. SkColor is the type used to
+    specify colors in SkPaint and in gradients.
+*/
+typedef uint32_t SkColor;
+
+/** Return a SkColor value from 8 bit component values
+*/
+static inline SkColor SkColorSetARGBInline(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+    SkASSERT(a <= 255 && r <= 255 && g <= 255 && b <= 255);
+
+    return (a << 24) | (r << 16) | (g << 8) | (b << 0);
+}
+
+#define SkColorSetARGBMacro(a, r, g, b) \
+    static_cast<SkColor>( \
+        (static_cast<U8CPU>(a) << 24) | \
+        (static_cast<U8CPU>(r) << 16) | \
+        (static_cast<U8CPU>(g) << 8) | \
+        (static_cast<U8CPU>(b) << 0))
+
+/** gcc will generate static initializers for code of this form:
+ * static const SkColor kMyColor = SkColorSetARGB(0xFF, 0x01, 0x02, 0x03)
+ * if SkColorSetARGB() is a static inline, but not if it's a macro.
+ */
+#if defined(NDEBUG)
+#define SkColorSetARGB(a, r, g, b) SkColorSetARGBMacro(a, r, g, b)
+#else
+#define SkColorSetARGB(a, r, g, b) SkColorSetARGBInline(a, r, g, b)
+#endif
+
+/** Return a SkColor value from 8 bit component values, with an implied value
+    of 0xFF for alpha (fully opaque)
+*/
+#define SkColorSetRGB(r, g, b)  SkColorSetARGB(0xFF, r, g, b)
+
+/** return the alpha byte from a SkColor value */
+#define SkColorGetA(color)      (((color) >> 24) & 0xFF)
+/** return the red byte from a SkColor value */
+#define SkColorGetR(color)      (((color) >> 16) & 0xFF)
+/** return the green byte from a SkColor value */
+#define SkColorGetG(color)      (((color) >>  8) & 0xFF)
+/** return the blue byte from a SkColor value */
+#define SkColorGetB(color)      (((color) >>  0) & 0xFF)
+
+static inline SkColor SkColorSetA(SkColor c, U8CPU a) {
+    return (c & 0x00FFFFFF) | (a << 24);
+}
+
+// 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_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
+
+////////////////////////////////////////////////////////////////////////
+
+/** Convert RGB components to HSV.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    @param red  red component value [0..255]
+    @param green  green component value [0..255]
+    @param blue  blue component value [0..255]
+    @param hsv  3 element array which holds the resulting HSV components.
+*/
+SK_API void SkRGBToHSV(U8CPU red, U8CPU green, U8CPU blue, SkScalar hsv[3]);
+
+/** Convert the argb color to its HSV components.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    @param color the argb color to convert. Note: the alpha component is ignored.
+    @param hsv  3 element array which holds the resulting HSV components.
+*/
+static inline void SkColorToHSV(SkColor color, SkScalar hsv[3])
+{
+    SkRGBToHSV(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), hsv);
+}
+
+/** Convert HSV components to an ARGB color. The alpha component is passed through unchanged.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    If hsv values are out of range, they are pinned.
+    @param alpha the alpha component of the returned argb color.
+    @param hsv  3 element array which holds the input HSV components.
+    @return the resulting argb color
+*/
+SK_API SkColor SkHSVToColor(U8CPU alpha, const SkScalar hsv[3]);
+
+/** Convert HSV components to an ARGB color. The alpha component set to 0xFF.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    If hsv values are out of range, they are pinned.
+    @param hsv  3 element array which holds the input HSV components.
+    @return the resulting argb color
+*/
+static inline SkColor SkHSVToColor(const SkScalar hsv[3])
+{
+    return SkHSVToColor(0xFF, hsv);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+/** 32 bit ARGB color value, premultiplied. The byte order for this value is
+    configuration dependent, matching the format of kARGB32 bitmaps. This is different
+    from SkColor, which is nonpremultiplied, and is always in the same byte order.
+*/
+typedef uint32_t SkPMColor;
+
+/** Return a SkPMColor value from unpremultiplied 8 bit component values
+*/
+SK_API SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+/** Return a SkPMColor value from a SkColor value. This is done by multiplying the color
+    components by the color's alpha, and by arranging the bytes in a configuration
+    dependent order, to match the format of kARGB32 bitmaps.
+*/
+SK_API SkPMColor SkPreMultiplyColor(SkColor c);
+
+/** Define a function pointer type for combining two premultiplied colors
+*/
+typedef SkPMColor (*SkXfermodeProc)(SkPMColor src, SkPMColor dst);
+
+/** Define a function pointer type for combining a premultiplied src color
+    and a 16bit device color.
+*/
+typedef uint16_t (*SkXfermodeProc16)(SkPMColor src, uint16_t dst);
+
+#endif
+
diff --git a/legacy/include/core/SkColorFilter.h b/legacy/include/core/SkColorFilter.h
new file mode 100644
index 0000000..97db5cc
--- /dev/null
+++ b/legacy/include/core/SkColorFilter.h
@@ -0,0 +1,159 @@
+
+/*
+ * 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 SkColorFilter_DEFINED
+#define SkColorFilter_DEFINED
+
+#include "SkColor.h"
+#include "SkFlattenable.h"
+#include "SkXfermode.h"
+
+class SK_API SkColorFilter : public SkFlattenable {
+public:
+    /**
+     *  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);
+
+    /**
+     *  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]);
+
+    /**
+     *  If the filter can be represented by per-component table, return true,
+     *  and if table is not null, copy the bitmap containing the table into it.
+     *
+     *  The table bitmap will be in SkBitmap::kA8_Config. Each row corresponding
+     *  to each component in ARGB order. e.g. row[0] == alpha, row[1] == red,
+     *  etc. To transform a color, you (logically) perform the following:
+     *
+     *      a' = *table.getAddr8(a, 0);
+     *      r' = *table.getAddr8(r, 1);
+     *      g' = *table.getAddr8(g, 2);
+     *      b' = *table.getAddr8(b, 3);
+     *
+     *  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);
+
+    /** Called with a scanline of colors, as if there was a shader installed.
+        The implementation writes out its filtered version into result[].
+        Note: shader and result may be the same buffer.
+        @param src      array of colors, possibly generated by a shader
+        @param count    the number of entries in the src[] and result[] arrays
+        @param result   written by the filter
+    */
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) = 0;
+    /** Called with a scanline of colors, as if there was a shader installed.
+        The implementation writes out its filtered version into result[].
+        Note: shader and result may be the same buffer.
+        @param src      array of colors, possibly generated by a shader
+        @param count    the number of entries in the src[] and result[] arrays
+        @param result   written by the filter
+    */
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]);
+
+    enum Flags {
+        /** If set the filter methods will not change the alpha channel of the
+            colors.
+        */
+        kAlphaUnchanged_Flag = 0x01,
+        /** If set, this subclass implements filterSpan16(). If this flag is
+            set, then kAlphaUnchanged_Flag must also be set.
+        */
+        kHasFilter16_Flag    = 0x02
+    };
+
+    /** Returns the flags for this filter. Override in subclasses to return
+        custom flags.
+    */
+    virtual uint32_t getFlags() { return 0; }
+
+    /**
+     *  Apply this colorfilter to the specified SkColor. This routine handles
+     *  converting to SkPMColor, calling the filter, and then converting back
+     *  to SkColor. This method is not virtual, but will call filterSpan()
+     *   which is virtual.
+     */
+    SkColor filterColor(SkColor);
+
+
+    /** Create a colorfilter that uses the specified color and mode.
+        If the Mode is DST, this function will return NULL (since that
+        mode will have no effect on the result).
+        @param c    The source color used with the specified mode
+        @param mode The xfermode mode that is applied to each color in
+                        the colorfilter's filterSpan[16,32] methods
+        @return colorfilter object that applies the src color and mode,
+                    or NULL if the mode will have no effect.
+    */
+    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()
+protected:
+    SkColorFilter() {}
+    SkColorFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
+
+private:
+    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/legacy/include/core/SkColorPriv.h b/legacy/include/core/SkColorPriv.h
new file mode 100644
index 0000000..e51b0b9
--- /dev/null
+++ b/legacy/include/core/SkColorPriv.h
@@ -0,0 +1,829 @@
+
+/*
+ * 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 SkColorPriv_DEFINED
+#define SkColorPriv_DEFINED
+
+// turn this own for extra debug checking when blending onto 565
+#ifdef SK_DEBUG
+    #define CHECK_FOR_565_OVERFLOW
+#endif
+
+#include "SkColor.h"
+#include "SkMath.h"
+
+/** 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.
+
+    In debugging, asserts that alpha is 0..255
+*/
+static inline unsigned SkAlpha255To256(U8CPU alpha) {
+    SkASSERT(SkToU8(alpha) == alpha);
+    // this one assues that blending on top of an opaque dst keeps it that way
+    // even though it is less accurate than a+(a>>7) for non-opaque dsts
+    return alpha + 1;
+}
+
+/** Multiplify value by 0..256, and shift the result down 8
+    (i.e. return (value * alpha256) >> 8)
+ */
+#define SkAlphaMul(value, alpha256)     (SkMulS16(value, alpha256) >> 8)
+
+//  The caller may want negative values, so keep all params signed (int)
+//  so we don't accidentally slip into unsigned math and lose the sign
+//  extension when we shift (in SkAlphaMul)
+static inline int SkAlphaBlend(int src, int dst, int scale256) {
+    SkASSERT((unsigned)scale256 <= 256);
+    return dst + SkAlphaMul(src - dst, scale256);
+}
+
+/**
+ *  Returns (src * alpha + dst * (255 - alpha)) / 255
+ *
+ *  This is more accurate than SkAlphaBlend, but slightly slower
+ */
+static inline int SkAlphaBlend255(S16CPU src, S16CPU dst, U8CPU alpha) {
+    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;
+}
+
+#define SK_R16_BITS     5
+#define SK_G16_BITS     6
+#define SK_B16_BITS     5
+
+#define SK_R16_SHIFT    (SK_B16_BITS + SK_G16_BITS)
+#define SK_G16_SHIFT    (SK_B16_BITS)
+#define SK_B16_SHIFT    0
+
+#define SK_R16_MASK     ((1 << SK_R16_BITS) - 1)
+#define SK_G16_MASK     ((1 << SK_G16_BITS) - 1)
+#define SK_B16_MASK     ((1 << SK_B16_BITS) - 1)
+
+#define SkGetPackedR16(color)   (((unsigned)(color) >> SK_R16_SHIFT) & SK_R16_MASK)
+#define SkGetPackedG16(color)   (((unsigned)(color) >> SK_G16_SHIFT) & SK_G16_MASK)
+#define SkGetPackedB16(color)   (((unsigned)(color) >> SK_B16_SHIFT) & SK_B16_MASK)
+
+#define SkR16Assert(r)  SkASSERT((unsigned)(r) <= SK_R16_MASK)
+#define SkG16Assert(g)  SkASSERT((unsigned)(g) <= SK_G16_MASK)
+#define SkB16Assert(b)  SkASSERT((unsigned)(b) <= SK_B16_MASK)
+
+static inline uint16_t SkPackRGB16(unsigned r, unsigned g, unsigned b) {
+    SkASSERT(r <= SK_R16_MASK);
+    SkASSERT(g <= SK_G16_MASK);
+    SkASSERT(b <= SK_B16_MASK);
+
+    return SkToU16((r << SK_R16_SHIFT) | (g << SK_G16_SHIFT) | (b << SK_B16_SHIFT));
+}
+
+#define SK_R16_MASK_IN_PLACE        (SK_R16_MASK << SK_R16_SHIFT)
+#define SK_G16_MASK_IN_PLACE        (SK_G16_MASK << SK_G16_SHIFT)
+#define SK_B16_MASK_IN_PLACE        (SK_B16_MASK << SK_B16_SHIFT)
+
+/** Expand the 16bit color into a 32bit value that can be scaled all at once
+    by a value up to 32. Used in conjunction with SkCompact_rgb_16.
+*/
+static inline uint32_t SkExpand_rgb_16(U16CPU c) {
+    SkASSERT(c == (uint16_t)c);
+
+    return ((c & SK_G16_MASK_IN_PLACE) << 16) | (c & ~SK_G16_MASK_IN_PLACE);
+}
+
+/** Compress an expanded value (from SkExpand_rgb_16) back down to a 16bit
+    color value. The computation yields only 16bits of valid data, but we claim
+    to return 32bits, so that the compiler won't generate extra instructions to
+    "clean" the top 16bits. However, the top 16 can contain garbage, so it is
+    up to the caller to safely ignore them.
+*/
+static inline U16CPU SkCompact_rgb_16(uint32_t c) {
+    return ((c >> 16) & SK_G16_MASK_IN_PLACE) | (c & ~SK_G16_MASK_IN_PLACE);
+}
+
+/** Scale the 16bit color value by the 0..256 scale parameter.
+    The computation yields only 16bits of valid data, but we claim
+    to return 32bits, so that the compiler won't generate extra instructions to
+    "clean" the top 16bits.
+*/
+static inline U16CPU SkAlphaMulRGB16(U16CPU c, unsigned scale) {
+    return SkCompact_rgb_16(SkExpand_rgb_16(c) * (scale >> 3) >> 5);
+}
+
+// this helper explicitly returns a clean 16bit value (but slower)
+#define SkAlphaMulRGB16_ToU16(c, s)  (uint16_t)SkAlphaMulRGB16(c, s)
+
+/** Blend src and dst 16bit colors by the 0..256 scale parameter.
+    The computation yields only 16bits of valid data, but we claim
+    to return 32bits, so that the compiler won't generate extra instructions to
+    "clean" the top 16bits.
+*/
+static inline U16CPU SkBlendRGB16(U16CPU src, U16CPU dst, int srcScale) {
+    SkASSERT((unsigned)srcScale <= 256);
+
+    srcScale >>= 3;
+
+    uint32_t src32 = SkExpand_rgb_16(src);
+    uint32_t dst32 = SkExpand_rgb_16(dst);
+    return SkCompact_rgb_16(dst32 + ((src32 - dst32) * srcScale >> 5));
+}
+
+static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[],
+                                int srcScale, int count) {
+    SkASSERT(count > 0);
+    SkASSERT((unsigned)srcScale <= 256);
+
+    srcScale >>= 3;
+
+    do {
+        uint32_t src32 = SkExpand_rgb_16(*src++);
+        uint32_t dst32 = SkExpand_rgb_16(*dst);
+        *dst++ = SkCompact_rgb_16(dst32 + ((src32 - dst32) * srcScale >> 5));
+    } while (--count > 0);
+}
+
+#ifdef SK_DEBUG
+    static inline U16CPU SkRGB16Add(U16CPU a, U16CPU b) {
+        SkASSERT(SkGetPackedR16(a) + SkGetPackedR16(b) <= SK_R16_MASK);
+        SkASSERT(SkGetPackedG16(a) + SkGetPackedG16(b) <= SK_G16_MASK);
+        SkASSERT(SkGetPackedB16(a) + SkGetPackedB16(b) <= SK_B16_MASK);
+
+        return a + b;
+    }
+#else
+    #define SkRGB16Add(a, b)  ((a) + (b))
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SK_A32_BITS     8
+#define SK_R32_BITS     8
+#define SK_G32_BITS     8
+#define SK_B32_BITS     8
+
+#define SK_A32_MASK     ((1 << SK_A32_BITS) - 1)
+#define SK_R32_MASK     ((1 << SK_R32_BITS) - 1)
+#define SK_G32_MASK     ((1 << SK_G32_BITS) - 1)
+#define SK_B32_MASK     ((1 << SK_B32_BITS) - 1)
+
+#define SkGetPackedA32(packed)      ((uint32_t)((packed) << (24 - SK_A32_SHIFT)) >> 24)
+#define SkGetPackedR32(packed)      ((uint32_t)((packed) << (24 - SK_R32_SHIFT)) >> 24)
+#define SkGetPackedG32(packed)      ((uint32_t)((packed) << (24 - SK_G32_SHIFT)) >> 24)
+#define SkGetPackedB32(packed)      ((uint32_t)((packed) << (24 - SK_B32_SHIFT)) >> 24)
+
+#define SkA32Assert(a)  SkASSERT((unsigned)(a) <= SK_A32_MASK)
+#define SkR32Assert(r)  SkASSERT((unsigned)(r) <= SK_R32_MASK)
+#define SkG32Assert(g)  SkASSERT((unsigned)(g) <= SK_G32_MASK)
+#define SkB32Assert(b)  SkASSERT((unsigned)(b) <= SK_B32_MASK)
+
+#ifdef SK_DEBUG
+    static inline void SkPMColorAssert(SkPMColor c) {
+        unsigned a = SkGetPackedA32(c);
+        unsigned r = SkGetPackedR32(c);
+        unsigned g = SkGetPackedG32(c);
+        unsigned b = SkGetPackedB32(c);
+
+        SkA32Assert(a);
+        SkASSERT(r <= a);
+        SkASSERT(g <= a);
+        SkASSERT(b <= a);
+    }
+#else
+    #define SkPMColorAssert(c)
+#endif
+
+/**
+ *  Pack the components into a SkPMColor, checking (in the debug version) that
+ *  the components are 0..255, and are already premultiplied (i.e. alpha >= color)
+ */
+static inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    SkA32Assert(a);
+    SkASSERT(r <= a);
+    SkASSERT(g <= a);
+    SkASSERT(b <= a);
+
+    return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
+           (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
+}
+
+/**
+ * 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);
+
+    unsigned a = SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale);
+    unsigned r = SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale);
+    unsigned g = SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale);
+    unsigned b = SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale);
+
+    return SkPackARGB32(a, r, g, b);
+}
+
+/**
+ * 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:
+ *   (src, dst, 0) returns dst
+ *   (src, dst, 0xFF) returns src
+ * ** Does not match the results of SkFourByteInterp() because we use
+ * a more accurate scale computation!
+ * TODO: migrate Skia function to using an accurate 255->266 alpha
+ * conversion.
+ */
+static inline SkPMColor SkFastFourByteInterp(SkPMColor src,
+                                             SkPMColor dst,
+                                             U8CPU srcWeight) {
+    SkASSERT(srcWeight < 256);
+
+    // Reorders ARGB to AG-RB in order to reduce the number of operations.
+    const uint32_t mask = 0xFF00FF;
+    uint32_t src_rb = src & mask;
+    uint32_t src_ag = (src >> 8) & mask;
+    uint32_t dst_rb = dst & mask;
+    uint32_t dst_ag = (dst >> 8) & mask;
+
+    // scale = srcWeight + (srcWeight >> 7) is more accurate than
+    // scale = srcWeight + 1, but 7% slower
+    int scale = srcWeight + (srcWeight >> 7);
+
+    uint32_t ret_rb = src_rb * scale + (256 - scale) * dst_rb;
+    uint32_t ret_ag = src_ag * scale + (256 - scale) * dst_ag;
+
+    return (ret_ag & ~mask) | ((ret_rb & ~mask) >> 8);
+}
+
+/**
+ *  Same as SkPackARGB32, but this version guarantees to not check that the
+ *  values are premultiplied in the debug version.
+ */
+static inline SkPMColor SkPackARGB32NoCheck(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
+           (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
+}
+
+static inline
+SkPMColor SkPremultiplyARGBInline(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    SkA32Assert(a);
+    SkA32Assert(r);
+    SkA32Assert(g);
+    SkA32Assert(b);
+
+    if (a != 255) {
+        r = SkMulDiv255Round(r, a);
+        g = SkMulDiv255Round(g, a);
+        b = SkMulDiv255Round(b, a);
+    }
+    return SkPackARGB32(a, r, g, b);
+}
+
+SK_API extern const uint32_t gMask_00FF00FF;
+
+static inline uint32_t SkAlphaMulQ(uint32_t c, unsigned scale) {
+    uint32_t mask = gMask_00FF00FF;
+//    uint32_t mask = 0xFF00FF;
+
+    uint32_t rb = ((c & mask) * scale) >> 8;
+    uint32_t ag = ((c >> 8) & mask) * scale;
+    return (rb & mask) | (ag & ~mask);
+}
+
+static inline SkPMColor SkPMSrcOver(SkPMColor src, SkPMColor dst) {
+    return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
+}
+
+static inline SkPMColor SkBlendARGB32(SkPMColor src, SkPMColor dst, U8CPU aa) {
+    SkASSERT((unsigned)aa <= 255);
+
+    unsigned src_scale = SkAlpha255To256(aa);
+    unsigned dst_scale = SkAlpha255To256(255 - SkAlphaMul(SkGetPackedA32(src), src_scale));
+
+    return SkAlphaMulQ(src, src_scale) + SkAlphaMulQ(dst, dst_scale);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Convert a 32bit pixel to a 16bit pixel (no dither)
+
+#define SkR32ToR16_MACRO(r)   ((unsigned)(r) >> (SK_R32_BITS - SK_R16_BITS))
+#define SkG32ToG16_MACRO(g)   ((unsigned)(g) >> (SK_G32_BITS - SK_G16_BITS))
+#define SkB32ToB16_MACRO(b)   ((unsigned)(b) >> (SK_B32_BITS - SK_B16_BITS))
+
+#ifdef SK_DEBUG
+    static inline unsigned SkR32ToR16(unsigned r) {
+        SkR32Assert(r);
+        return SkR32ToR16_MACRO(r);
+    }
+    static inline unsigned SkG32ToG16(unsigned g) {
+        SkG32Assert(g);
+        return SkG32ToG16_MACRO(g);
+    }
+    static inline unsigned SkB32ToB16(unsigned b) {
+        SkB32Assert(b);
+        return SkB32ToB16_MACRO(b);
+    }
+#else
+    #define SkR32ToR16(r)   SkR32ToR16_MACRO(r)
+    #define SkG32ToG16(g)   SkG32ToG16_MACRO(g)
+    #define SkB32ToB16(b)   SkB32ToB16_MACRO(b)
+#endif
+
+#define SkPacked32ToR16(c)  (((unsigned)(c) >> (SK_R32_SHIFT + SK_R32_BITS - SK_R16_BITS)) & SK_R16_MASK)
+#define SkPacked32ToG16(c)  (((unsigned)(c) >> (SK_G32_SHIFT + SK_G32_BITS - SK_G16_BITS)) & SK_G16_MASK)
+#define SkPacked32ToB16(c)  (((unsigned)(c) >> (SK_B32_SHIFT + SK_B32_BITS - SK_B16_BITS)) & SK_B16_MASK)
+
+static inline U16CPU SkPixel32ToPixel16(SkPMColor c) {
+    unsigned r = ((c >> (SK_R32_SHIFT + (8 - SK_R16_BITS))) & SK_R16_MASK) << SK_R16_SHIFT;
+    unsigned g = ((c >> (SK_G32_SHIFT + (8 - SK_G16_BITS))) & SK_G16_MASK) << SK_G16_SHIFT;
+    unsigned b = ((c >> (SK_B32_SHIFT + (8 - SK_B16_BITS))) & SK_B16_MASK) << SK_B16_SHIFT;
+    return r | g | b;
+}
+
+static inline U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b) {
+    return  (SkR32ToR16(r) << SK_R16_SHIFT) |
+            (SkG32ToG16(g) << SK_G16_SHIFT) |
+            (SkB32ToB16(b) << SK_B16_SHIFT);
+}
+
+#define SkPixel32ToPixel16_ToU16(src)   SkToU16(SkPixel32ToPixel16(src))
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Fast dither from 32->16
+
+#define SkShouldDitherXY(x, y)  (((x) ^ (y)) & 1)
+
+static inline uint16_t SkDitherPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b) {
+    r = ((r << 1) - ((r >> (8 - SK_R16_BITS) << (8 - SK_R16_BITS)) | (r >> SK_R16_BITS))) >> (8 - SK_R16_BITS);
+    g = ((g << 1) - ((g >> (8 - SK_G16_BITS) << (8 - SK_G16_BITS)) | (g >> SK_G16_BITS))) >> (8 - SK_G16_BITS);
+    b = ((b << 1) - ((b >> (8 - SK_B16_BITS) << (8 - SK_B16_BITS)) | (b >> SK_B16_BITS))) >> (8 - SK_B16_BITS);
+
+    return SkPackRGB16(r, g, b);
+}
+
+static inline uint16_t SkDitherPixel32ToPixel16(SkPMColor c) {
+    return SkDitherPack888ToRGB16(SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c));
+}
+
+/*  Return c in expanded_rgb_16 format, but also scaled up by 32 (5 bits)
+    It is now suitable for combining with a scaled expanded_rgb_16 color
+    as in SkSrcOver32To16().
+    We must do this 565 high-bit replication, in order for the subsequent add
+    to saturate properly (and not overflow). If we take the 8 bits as is, it is
+    possible to overflow.
+*/
+static inline uint32_t SkPMColorToExpanded16x5(SkPMColor c) {
+    unsigned sr = SkPacked32ToR16(c);
+    unsigned sg = SkPacked32ToG16(c);
+    unsigned sb = SkPacked32ToB16(c);
+
+    sr = (sr << 5) | sr;
+    sg = (sg << 5) | (sg >> 1);
+    sb = (sb << 5) | sb;
+    return (sr << 11) | (sg << 21) | (sb << 0);
+}
+
+/*  SrcOver the 32bit src color with the 16bit dst, returning a 16bit value
+    (with dirt in the high 16bits, so caller beware).
+*/
+static inline U16CPU SkSrcOver32To16(SkPMColor src, uint16_t dst) {
+    unsigned sr = SkGetPackedR32(src);
+    unsigned sg = SkGetPackedG32(src);
+    unsigned sb = SkGetPackedB32(src);
+
+    unsigned dr = SkGetPackedR16(dst);
+    unsigned dg = SkGetPackedG16(dst);
+    unsigned db = SkGetPackedB16(dst);
+
+    unsigned isa = 255 - SkGetPackedA32(src);
+
+    dr = (sr + SkMul16ShiftRound(dr, isa, SK_R16_BITS)) >> (8 - SK_R16_BITS);
+    dg = (sg + SkMul16ShiftRound(dg, isa, SK_G16_BITS)) >> (8 - SK_G16_BITS);
+    db = (sb + SkMul16ShiftRound(db, isa, SK_B16_BITS)) >> (8 - SK_B16_BITS);
+
+    return SkPackRGB16(dr, dg, db);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Convert a 16bit pixel to a 32bit pixel
+
+static inline unsigned SkR16ToR32(unsigned r) {
+    return (r << (8 - SK_R16_BITS)) | (r >> (2 * SK_R16_BITS - 8));
+}
+
+static inline unsigned SkG16ToG32(unsigned g) {
+    return (g << (8 - SK_G16_BITS)) | (g >> (2 * SK_G16_BITS - 8));
+}
+
+static inline unsigned SkB16ToB32(unsigned b) {
+    return (b << (8 - SK_B16_BITS)) | (b >> (2 * SK_B16_BITS - 8));
+}
+
+#define SkPacked16ToR32(c)      SkR16ToR32(SkGetPackedR16(c))
+#define SkPacked16ToG32(c)      SkG16ToG32(SkGetPackedG16(c))
+#define SkPacked16ToB32(c)      SkB16ToB32(SkGetPackedB16(c))
+
+static inline SkPMColor SkPixel16ToPixel32(U16CPU src) {
+    SkASSERT(src == SkToU16(src));
+
+    unsigned    r = SkPacked16ToR32(src);
+    unsigned    g = SkPacked16ToG32(src);
+    unsigned    b = SkPacked16ToB32(src);
+
+    SkASSERT((r >> (8 - SK_R16_BITS)) == SkGetPackedR16(src));
+    SkASSERT((g >> (8 - SK_G16_BITS)) == SkGetPackedG16(src));
+    SkASSERT((b >> (8 - SK_B16_BITS)) == SkGetPackedB16(src));
+
+    return SkPackARGB32(0xFF, r, g, b);
+}
+
+// similar to SkPixel16ToPixel32, but returns SkColor instead of SkPMColor
+static inline SkColor SkPixel16ToColor(U16CPU src) {
+    SkASSERT(src == SkToU16(src));
+
+    unsigned    r = SkPacked16ToR32(src);
+    unsigned    g = SkPacked16ToG32(src);
+    unsigned    b = SkPacked16ToB32(src);
+
+    SkASSERT((r >> (8 - SK_R16_BITS)) == SkGetPackedR16(src));
+    SkASSERT((g >> (8 - SK_G16_BITS)) == SkGetPackedG16(src));
+    SkASSERT((b >> (8 - SK_B16_BITS)) == SkGetPackedB16(src));
+
+    return SkColorSetRGB(r, g, b);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint16_t SkPMColor16;
+
+// Put in OpenGL order (r g b a)
+#define SK_A4444_SHIFT    0
+#define SK_R4444_SHIFT    12
+#define SK_G4444_SHIFT    8
+#define SK_B4444_SHIFT    4
+
+#define SkA32To4444(a)  ((unsigned)(a) >> 4)
+#define SkR32To4444(r)  ((unsigned)(r) >> 4)
+#define SkG32To4444(g)  ((unsigned)(g) >> 4)
+#define SkB32To4444(b)  ((unsigned)(b) >> 4)
+
+static inline U8CPU SkReplicateNibble(unsigned nib) {
+    SkASSERT(nib <= 0xF);
+    return (nib << 4) | nib;
+}
+
+#define SkA4444ToA32(a)     SkReplicateNibble(a)
+#define SkR4444ToR32(r)     SkReplicateNibble(r)
+#define SkG4444ToG32(g)     SkReplicateNibble(g)
+#define SkB4444ToB32(b)     SkReplicateNibble(b)
+
+#define SkGetPackedA4444(c)     (((unsigned)(c) >> SK_A4444_SHIFT) & 0xF)
+#define SkGetPackedR4444(c)     (((unsigned)(c) >> SK_R4444_SHIFT) & 0xF)
+#define SkGetPackedG4444(c)     (((unsigned)(c) >> SK_G4444_SHIFT) & 0xF)
+#define SkGetPackedB4444(c)     (((unsigned)(c) >> SK_B4444_SHIFT) & 0xF)
+
+#define SkPacked4444ToA32(c)    SkReplicateNibble(SkGetPackedA4444(c))
+#define SkPacked4444ToR32(c)    SkReplicateNibble(SkGetPackedR4444(c))
+#define SkPacked4444ToG32(c)    SkReplicateNibble(SkGetPackedG4444(c))
+#define SkPacked4444ToB32(c)    SkReplicateNibble(SkGetPackedB4444(c))
+
+#ifdef SK_DEBUG
+static inline void SkPMColor16Assert(U16CPU c) {
+    unsigned a = SkGetPackedA4444(c);
+    unsigned r = SkGetPackedR4444(c);
+    unsigned g = SkGetPackedG4444(c);
+    unsigned b = SkGetPackedB4444(c);
+
+    SkASSERT(a <= 0xF);
+    SkASSERT(r <= a);
+    SkASSERT(g <= a);
+    SkASSERT(b <= a);
+}
+#else
+#define SkPMColor16Assert(c)
+#endif
+
+static inline unsigned SkAlpha15To16(unsigned a) {
+    SkASSERT(a <= 0xF);
+    return a + (a >> 3);
+}
+
+#ifdef SK_DEBUG
+    static inline int SkAlphaMul4(int value, int scale) {
+        SkASSERT((unsigned)scale <= 0x10);
+        return value * scale >> 4;
+    }
+#else
+    #define SkAlphaMul4(value, scale)   ((value) * (scale) >> 4)
+#endif
+
+static inline unsigned SkR4444ToR565(unsigned r) {
+    SkASSERT(r <= 0xF);
+    return (r << (SK_R16_BITS - 4)) | (r >> (8 - SK_R16_BITS));
+}
+
+static inline unsigned SkG4444ToG565(unsigned g) {
+    SkASSERT(g <= 0xF);
+    return (g << (SK_G16_BITS - 4)) | (g >> (8 - SK_G16_BITS));
+}
+
+static inline unsigned SkB4444ToB565(unsigned b) {
+    SkASSERT(b <= 0xF);
+    return (b << (SK_B16_BITS - 4)) | (b >> (8 - SK_B16_BITS));
+}
+
+static inline SkPMColor16 SkPackARGB4444(unsigned a, unsigned r,
+                                         unsigned g, unsigned b) {
+    SkASSERT(a <= 0xF);
+    SkASSERT(r <= a);
+    SkASSERT(g <= a);
+    SkASSERT(b <= a);
+
+    return (SkPMColor16)((a << SK_A4444_SHIFT) | (r << SK_R4444_SHIFT) |
+                         (g << SK_G4444_SHIFT) | (b << SK_B4444_SHIFT));
+}
+
+extern const uint16_t gMask_0F0F;
+
+static inline U16CPU SkAlphaMulQ4(U16CPU c, unsigned scale) {
+    SkASSERT(scale <= 16);
+
+    const unsigned mask = 0xF0F;    //gMask_0F0F;
+
+#if 0
+    unsigned rb = ((c & mask) * scale) >> 4;
+    unsigned ag = ((c >> 4) & mask) * scale;
+    return (rb & mask) | (ag & ~mask);
+#else
+    c = (c & mask) | ((c & (mask << 4)) << 12);
+    c = c * scale >> 4;
+    return (c & mask) | ((c >> 12) & (mask << 4));
+#endif
+}
+
+/** Expand the SkPMColor16 color into a 32bit value that can be scaled all at
+    once by a value up to 16. Used in conjunction with SkCompact_4444.
+*/
+static inline uint32_t SkExpand_4444(U16CPU c) {
+    SkASSERT(c == (uint16_t)c);
+
+    const unsigned mask = 0xF0F;    //gMask_0F0F;
+    return (c & mask) | ((c & ~mask) << 12);
+}
+
+/** Compress an expanded value (from SkExpand_4444) back down to a SkPMColor16.
+    NOTE: this explicitly does not clean the top 16 bits (which may be garbage).
+    It does this for speed, since if it is being written directly to 16bits of
+    memory, the top 16bits will be ignored. Casting the result to uint16_t here
+    would add 2 more instructions, slow us down. It is up to the caller to
+    perform the cast if needed.
+*/
+static inline U16CPU SkCompact_4444(uint32_t c) {
+    const unsigned mask = 0xF0F;    //gMask_0F0F;
+    return (c & mask) | ((c >> 12) & ~mask);
+}
+
+static inline uint16_t SkSrcOver4444To16(SkPMColor16 s, uint16_t d) {
+    unsigned sa = SkGetPackedA4444(s);
+    unsigned sr = SkR4444ToR565(SkGetPackedR4444(s));
+    unsigned sg = SkG4444ToG565(SkGetPackedG4444(s));
+    unsigned sb = SkB4444ToB565(SkGetPackedB4444(s));
+
+    // To avoid overflow, we have to clear the low bit of the synthetic sg
+    // if the src alpha is <= 7.
+    // to see why, try blending 0x4444 on top of 565-white and watch green
+    // overflow (sum == 64)
+    sg &= ~(~(sa >> 3) & 1);
+
+    unsigned scale = SkAlpha15To16(15 - sa);
+    unsigned dr = SkAlphaMul4(SkGetPackedR16(d), scale);
+    unsigned dg = SkAlphaMul4(SkGetPackedG16(d), scale);
+    unsigned db = SkAlphaMul4(SkGetPackedB16(d), scale);
+
+#if 0
+    if (sg + dg > 63) {
+        SkDebugf("---- SkSrcOver4444To16 src=%x dst=%x scale=%d, sg=%d dg=%d\n", s, d, scale, sg, dg);
+    }
+#endif
+    return SkPackRGB16(sr + dr, sg + dg, sb + db);
+}
+
+static inline uint16_t SkBlend4444To16(SkPMColor16 src, uint16_t dst, int scale16) {
+    SkASSERT((unsigned)scale16 <= 16);
+
+    return SkSrcOver4444To16(SkAlphaMulQ4(src, scale16), dst);
+}
+
+static inline uint16_t SkBlend4444(SkPMColor16 src, SkPMColor16 dst, int scale16) {
+    SkASSERT((unsigned)scale16 <= 16);
+
+    uint32_t src32 = SkExpand_4444(src) * scale16;
+    // the scaled srcAlpha is the bottom byte
+#ifdef SK_DEBUG
+    {
+        unsigned srcA = SkGetPackedA4444(src) * scale16;
+        SkASSERT(srcA == (src32 & 0xFF));
+    }
+#endif
+    unsigned dstScale = SkAlpha255To256(255 - (src32 & 0xFF)) >> 4;
+    uint32_t dst32 = SkExpand_4444(dst) * dstScale;
+    return SkCompact_4444((src32 + dst32) >> 4);
+}
+
+static inline SkPMColor SkPixel4444ToPixel32(U16CPU c) {
+    uint32_t d = (SkGetPackedA4444(c) << SK_A32_SHIFT) |
+                 (SkGetPackedR4444(c) << SK_R32_SHIFT) |
+                 (SkGetPackedG4444(c) << SK_G32_SHIFT) |
+                 (SkGetPackedB4444(c) << SK_B32_SHIFT);
+    return d | (d << 4);
+}
+
+static inline SkPMColor16 SkPixel32ToPixel4444(SkPMColor c) {
+    return  (((c >> (SK_A32_SHIFT + 4)) & 0xF) << SK_A4444_SHIFT) |
+    (((c >> (SK_R32_SHIFT + 4)) & 0xF) << SK_R4444_SHIFT) |
+    (((c >> (SK_G32_SHIFT + 4)) & 0xF) << SK_G4444_SHIFT) |
+    (((c >> (SK_B32_SHIFT + 4)) & 0xF) << SK_B4444_SHIFT);
+}
+
+// cheap 2x2 dither
+static inline SkPMColor16 SkDitherARGB32To4444(U8CPU a, U8CPU r,
+                                               U8CPU g, U8CPU b) {
+    // to ensure that we stay a legal premultiplied color, we take the max()
+    // of the truncated and dithered alpha values. If we didn't, cases like
+    // SkDitherARGB32To4444(0x31, 0x2E, ...) would generate SkPackARGB4444(2, 3, ...)
+    // which is not legal premultiplied, since a < color
+    unsigned dithered_a = ((a << 1) - ((a >> 4 << 4) | (a >> 4))) >> 4;
+    a = SkMax32(a >> 4, dithered_a);
+    // these we just dither in place
+    r = ((r << 1) - ((r >> 4 << 4) | (r >> 4))) >> 4;
+    g = ((g << 1) - ((g >> 4 << 4) | (g >> 4))) >> 4;
+    b = ((b << 1) - ((b >> 4 << 4) | (b >> 4))) >> 4;
+
+    return SkPackARGB4444(a, r, g, b);
+}
+
+static inline SkPMColor16 SkDitherPixel32To4444(SkPMColor c) {
+    return SkDitherARGB32To4444(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+}
+
+/*  Assumes 16bit is in standard RGBA order.
+    Transforms a normal ARGB_8888 into the same byte order as
+    expanded ARGB_4444, but keeps each component 8bits
+*/
+static inline uint32_t SkExpand_8888(SkPMColor c) {
+    return  (((c >> SK_R32_SHIFT) & 0xFF) << 24) |
+            (((c >> SK_G32_SHIFT) & 0xFF) <<  8) |
+            (((c >> SK_B32_SHIFT) & 0xFF) << 16) |
+            (((c >> SK_A32_SHIFT) & 0xFF) <<  0);
+}
+
+/*  Undo the operation of SkExpand_8888, turning the argument back into
+    a SkPMColor.
+*/
+static inline SkPMColor SkCompact_8888(uint32_t c) {
+    return  (((c >> 24) & 0xFF) << SK_R32_SHIFT) |
+            (((c >>  8) & 0xFF) << SK_G32_SHIFT) |
+            (((c >> 16) & 0xFF) << SK_B32_SHIFT) |
+            (((c >>  0) & 0xFF) << SK_A32_SHIFT);
+}
+
+/*  Like SkExpand_8888, this transforms a pmcolor into the expanded 4444 format,
+    but this routine just keeps the high 4bits of each component in the low
+    4bits of the result (just like a newly expanded PMColor16).
+*/
+static inline uint32_t SkExpand32_4444(SkPMColor c) {
+    return  (((c >> (SK_R32_SHIFT + 4)) & 0xF) << 24) |
+            (((c >> (SK_G32_SHIFT + 4)) & 0xF) <<  8) |
+            (((c >> (SK_B32_SHIFT + 4)) & 0xF) << 16) |
+            (((c >> (SK_A32_SHIFT + 4)) & 0xF) <<  0);
+}
+
+// takes two values and alternamtes them as part of a memset16
+// used for cheap 2x2 dithering when the colors are opaque
+void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int n);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline int SkUpscale31To32(int value) {
+    SkASSERT((unsigned)value <= 31);
+    return value + (value >> 4);
+}
+
+static inline int SkBlend32(int src, int dst, int scale) {
+    SkASSERT((unsigned)src <= 0xFF);
+    SkASSERT((unsigned)dst <= 0xFF);
+    SkASSERT((unsigned)scale <= 32);
+    return dst + ((src - dst) * scale >> 5);
+}
+
+static inline SkPMColor SkBlendLCD16(int srcA, int srcR, int srcG, int srcB,
+                                     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,
+                        SkBlend32(srcR, dstR, maskR),
+                        SkBlend32(srcG, dstG, maskG),
+                        SkBlend32(srcB, dstB, maskB));
+}
+
+static inline SkPMColor SkBlendLCD16Opaque(int srcR, int srcG, int srcB,
+                                           SkPMColor dst, uint16_t mask,
+                                           SkPMColor opaqueDst) { 
+    if (mask == 0) {
+        return dst;
+    }
+
+    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,
+                        SkBlend32(srcR, dstR, maskR),
+                        SkBlend32(srcG, dstG, maskG),
+                        SkBlend32(srcB, dstB, maskB));
+}
+
+static inline void SkBlitLCD16Row(SkPMColor dst[], const uint16_t src[],
+                                  SkColor color, int width, SkPMColor) {
+    int srcA = SkColorGetA(color);
+    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, 
+                                        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); 
+    }
+}
+
+#endif
+
diff --git a/legacy/include/core/SkColorShader.h b/legacy/include/core/SkColorShader.h
new file mode 100644
index 0000000..9b1fed3
--- /dev/null
+++ b/legacy/include/core/SkColorShader.h
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright 2007 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 SkColorShader_DEFINED
+#define SkColorShader_DEFINED
+
+#include "SkShader.h"
+
+/** \class SkColorShader
+    A Shader that represents a single color. In general, this effect can be
+    accomplished by just using the color field on the paint, but if an
+    actual shader object is needed, this provides that feature.
+*/
+class SK_API SkColorShader : public SkShader {
+public:
+    /** Create a ColorShader that will inherit its color from the Paint
+        at draw time.
+    */
+    SkColorShader();
+
+    /** Create a ColorShader that ignores the color in the paint, and uses the
+        specified color. Note: like all shaders, at draw time the paint's alpha
+        will be respected, and is applied to the specified color.
+    */
+    SkColorShader(SkColor c);
+
+    virtual ~SkColorShader();
+
+    virtual uint32_t getFlags() SK_OVERRIDE;
+    virtual uint8_t getSpan16Alpha() const SK_OVERRIDE;
+    virtual bool isOpaque() const SK_OVERRIDE;
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
+                            const SkMatrix& matrix) SK_OVERRIDE;
+    virtual void shadeSpan(int x, int y, SkPMColor span[], int count) SK_OVERRIDE;
+    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;
+
+    // we return false for this, use asAGradient
+    virtual BitmapType asABitmap(SkBitmap* outTexture,
+                                 SkMatrix* outMatrix,
+                                 TileMode xy[2],
+                                 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
+
+    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+
+protected:
+    SkColorShader(SkFlattenableReadBuffer&);
+
+    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    virtual Factory getFactory() SK_OVERRIDE;
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+    SkColor     fColor;         // ignored if fInheritColor is true
+    SkPMColor   fPMColor;       // cached after setContext()
+    uint32_t    fFlags;         // cached after setContext()
+    uint16_t    fColor16;       // cached after setContext()
+    SkBool8     fInheritColor;
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/legacy/include/core/SkComposeShader.h b/legacy/include/core/SkComposeShader.h
new file mode 100644
index 0000000..f243954
--- /dev/null
+++ b/legacy/include/core/SkComposeShader.h
@@ -0,0 +1,59 @@
+
+/*
+ * 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 SkComposeShader_DEFINED
+#define SkComposeShader_DEFINED
+
+#include "SkShader.h"
+
+class SkXfermode;
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+/** \class SkComposeShader
+    This subclass of shader returns the coposition of two other shaders, combined by
+    a xfermode.
+*/
+class SK_API SkComposeShader : public SkShader {
+public:
+    /** Create a new compose shader, given shaders A, B, and a combining xfermode mode.
+        When the xfermode is called, it will be given the result from shader A as its
+        "dst", and the result of from shader B as its "src".
+        mode->xfer32(sA_result, sB_result, ...)
+        @param shaderA  The colors from this shader are seen as the "dst" by the xfermode
+        @param shaderB  The colors from this shader are seen as the "src" by the xfermode
+        @param mode     The xfermode that combines the colors from the two shaders. If mode
+                        is null, then SRC_OVER is assumed.
+    */
+    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();
+
+protected:
+    SkComposeShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(SkComposeShader, (buffer)); }
+
+    SkShader*   fShaderA;
+    SkShader*   fShaderB;
+    SkXfermode* fMode;
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/legacy/include/core/SkData.h b/legacy/include/core/SkData.h
new file mode 100644
index 0000000..a134536
--- /dev/null
+++ b/legacy/include/core/SkData.h
@@ -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.
+ */
+
+
+
+#ifndef SkData_DEFINED
+#define SkData_DEFINED
+
+#include "SkRefCnt.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 {
+public:
+    /**
+     *  Returns the number of bytes stored.
+     */
+    size_t size() const { return fSize; }
+
+    /**
+     *  Returns the ptr to the data.
+     */
+    const void* data() const { return fPtr; }
+
+    /**
+     *  Like data(), returns a read-only ptr into the data, but in this case
+     *  it is cast to uint8_t*, to make it easy to add an offset to it.
+     */
+    const uint8_t* bytes() const {
+        return reinterpret_cast<const uint8_t*>(fPtr);
+    }
+
+    /**
+     *  Helper to copy a range of the data into a caller-provided buffer.
+     *  Returns the actual number of bytes copied, after clamping offset and
+     *  length to the size of the data. If buffer is NULL, it is ignored, and
+     *  only the computed number of bytes is returned.
+     */
+    size_t copyRange(size_t offset, size_t length, void* buffer) 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, taking the data ptr as is, and using the
+     *  releaseproc to free it. The proc may be NULL.
+     */
+    static SkData* NewWithProc(const void* data, size_t length,
+                               ReleaseProc proc, void* context);
+
+    /**
+     *  Create a new dataref, reference the data ptr as is, and calling
+     *  sk_free to delete it.
+     */
+    static SkData* NewFromMalloc(const void* data, size_t length);
+
+    /**
+     *  Create a new dataref using a subset of the data in the specified
+     *  src dataref.
+     */
+    static SkData* NewSubset(const SkData* src, size_t offset, size_t length);
+
+    /**
+     *  Returns a new empty dataref (or a reference to a shared empty dataref).
+     *  New or shared, the caller must see that unref() is eventually called.
+     */
+    static SkData* NewEmpty();
+
+private:
+    ReleaseProc fReleaseProc;
+    void*       fReleaseProcContext;
+
+    const void* fPtr;
+    size_t      fSize;
+
+    SkData(const void* ptr, size_t size, ReleaseProc, void* context);
+    ~SkData();
+};
+
+/**
+ *  Specialized version of SkAutoTUnref<SkData> for automatically unref-ing a
+ *  SkData. If the SkData is null, data(), bytes() and size() will return 0.
+ */
+class SkAutoDataUnref : SkNoncopyable {
+public:
+    SkAutoDataUnref(SkData* data) : fRef(data) {
+        if (data) {
+            fData = data->data();
+            fSize = data->size();
+        } else {
+            fData = NULL;
+            fSize = 0;
+        }
+    }
+    ~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;
+        }
+    }
+
+private:
+    SkData*     fRef;
+    const void* fData;
+    size_t      fSize;
+};
+
+#endif
diff --git a/legacy/include/core/SkDeque.h b/legacy/include/core/SkDeque.h
new file mode 100644
index 0000000..b4f420d
--- /dev/null
+++ b/legacy/include/core/SkDeque.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 SkDeque_DEFINED
+#define SkDeque_DEFINED
+
+#include "SkTypes.h"
+
+class SK_API SkDeque : SkNoncopyable {
+public:
+    explicit SkDeque(size_t elemSize);
+    SkDeque(size_t elemSize, void* storage, size_t storageSize);
+    ~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;
+
+    void* front() {
+        return (void*)((const SkDeque*)this)->front();
+    }
+
+    void* back() {
+        return (void*)((const SkDeque*)this)->back();
+    }
+
+    void* push_front();
+    void* push_back();
+
+    void pop_front();
+    void pop_back();
+
+private:
+    struct Head;
+
+public:
+    class F2BIter {
+    public:
+        /**
+         * Creates an uninitialized iterator. Must be reset()
+         */
+        F2BIter();
+
+        F2BIter(const SkDeque& d);
+        void* next();
+
+        void reset(const SkDeque& d);
+
+    private:
+        SkDeque::Head*  fHead;
+        char*           fPos;
+        size_t          fElemSize;
+    };
+
+private:
+    Head*   fFront;
+    Head*   fBack;
+    size_t  fElemSize;
+    void*   fInitialStorage;
+    int     fCount;
+
+    friend class Iter;
+};
+
+#endif
diff --git a/include/core/SkDescriptor.h b/legacy/include/core/SkDescriptor.h
similarity index 100%
rename from include/core/SkDescriptor.h
rename to legacy/include/core/SkDescriptor.h
diff --git a/legacy/include/core/SkDevice.h b/legacy/include/core/SkDevice.h
new file mode 100644
index 0000000..3303981
--- /dev/null
+++ b/legacy/include/core/SkDevice.h
@@ -0,0 +1,369 @@
+
+/*
+ * 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 SkDevice_DEFINED
+#define SkDevice_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+
+class SkClipStack;
+class SkDraw;
+struct SkIRect;
+class SkMatrix;
+class SkMetaData;
+class SkRegion;
+
+// This is an opaque class, not interpreted by skia
+class SkGpuRenderTarget;
+
+class SK_API SkDevice : public SkRefCnt {
+public:
+    /**
+     *  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);
+
+    /**
+     *  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.
+     */
+    SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque = false);
+
+    virtual ~SkDevice();
+
+    /**
+     *  Creates a device that is of the same type as this device (e.g. SW-raster,
+     *  GPU, or PDF). The backing store for this device is created automatically
+     *  (e.g. offscreen pixels or FBO or whatever is appropriate).
+     *
+     *  @param width    width of the device to create
+     *  @param height   height of the device to create
+     *  @param isOpaque performance hint, set to true if you know that you will
+     *                  draw into this device such that all of the pixels will
+     *                  be opaque.
+     */
+    SkDevice* createCompatibleDevice(SkBitmap::Config config,
+                                     int width, int height,
+                                     bool isOpaque);
+
+    SkMetaData& getMetaData();
+
+    enum Capabilities {
+        kGL_Capability     = 0x1,  //!< mask indicating GL support
+        kVector_Capability = 0x2,  //!< mask indicating a vector representation
+        kAll_Capabilities  = 0x3
+    };
+    virtual uint32_t getDeviceCapabilities() { return 0; }
+
+    /** Return the width of the device (in pixels).
+    */
+    virtual int width() const { return fBitmap.width(); }
+    /** Return the height of the device (in pixels).
+    */
+    virtual int height() const { return fBitmap.height(); }
+
+    /**
+     *  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
+     *  such as those associated with saveLayer may have a non-zero origin.
+     */
+    void getGlobalBounds(SkIRect* bounds) const;
+
+    /** Returns true if the device's bitmap's config treats every pixels as
+        implicitly opaque.
+    */
+    bool isOpaque() const { return fBitmap.isOpaque(); }
+
+    /** Return the bitmap config of the device's pixels
+    */
+    SkBitmap::Config config() const { return fBitmap.getConfig(); }
+
+    /** Return the bitmap associated with this device. Call this each time you need
+        to access the bitmap, as it notifies the subclass to perform any flushing
+        etc. before you examine the pixels.
+        @param changePixels set to true if the caller plans to change the pixels
+        @return the device's bitmap
+    */
+    const SkBitmap& accessBitmap(bool changePixels);
+
+    /**
+     *  DEPRECATED: This will be made protected once WebKit stops using it.
+     *              Instead use Canvas' writePixels method.
+     *
+     *  Similar to draw sprite, this method will copy the pixels in bitmap onto
+     *  the device, with the top/left corner specified by (x, y). The pixel
+     *  values in the device are completely replaced: there is no blending.
+     *
+     *  Currently if bitmap is backed by a texture this is a no-op. This may be
+     *  relaxed in the future.
+     *
+     *  If the bitmap has config kARGB_8888_Config then the config8888 param
+     *  will determines how the pixel valuess are intepreted. If the bitmap is
+     *  not kARGB_8888_Config then this parameter is ignored.
+     */
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+                             SkCanvas::Config8888 config8888 = SkCanvas::kNative_Premul_Config8888);
+
+    /**
+     * Return the device's associated gpu render target, or NULL.
+     */
+    virtual SkGpuRenderTarget* accessRenderTarget() { return NULL; }
+
+
+    /**
+     *  Return the device's origin: its offset in device coordinates from
+     *  the default origin in its canvas' matrix/clip
+     */
+    const SkIPoint& getOrigin() const { return fOrigin; }
+
+protected:
+    enum Usage {
+       kGeneral_Usage,
+       kSaveLayer_Usage, // <! internal use only
+    };
+
+    struct TextFlags {
+        uint32_t            fFlags;     // SkPaint::getFlags()
+        SkPaint::Hinting    fHinting;
+    };
+
+    /**
+     *  Device may filter the text flags for drawing text here. If it wants to
+     *  make a change to the specified values, it should write them into the
+     *  textflags parameter (output) and return true. If the paint is fine as
+     *  is, then ignore the textflags parameter and return false.
+     *
+     *  The baseclass SkDevice filters based on its depth and blitters.
+     */
+    virtual bool filterTextFlags(const SkPaint& paint, TextFlags*);
+
+    /**
+     *  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.
+     *
+     *  The clipstack is another view of the clip. It records the actual
+     *  geometry that went into building the region. It is present for devices
+     *  that want to parse it, but is not required: the region is a complete
+     *  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&) {}
+
+    /** Clears the entire device to the specified color (including alpha).
+     *  Ignores the clip.
+     */
+    virtual void clear(SkColor color);
+
+    /**
+     * Deprecated name for clear.
+     */
+    void eraseColor(SkColor eraseColor) { this->clear(eraseColor); }
+
+    /** These are called inside the per-device-layer loop for each draw call.
+     When these are called, we have already applied any saveLayer operations,
+     and are handling any looping from the paint, and any effects from the
+     DrawFilter.
+     */
+    virtual void drawPaint(const SkDraw&, const SkPaint& paint);
+    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
+                            const SkPoint[], const SkPaint& paint);
+    virtual void drawRect(const SkDraw&, const SkRect& r,
+                          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
+     *  may do this to implement helpers such as drawOval, by placing a temp
+     *  path on the stack to hold the representation of the oval.
+     *
+     *  If prePathMatrix is not null, it should logically be applied before any
+     *  stroking or other effects. If there are no effects on the paint that
+     *  affect the geometry/rasterization, then the pre matrix can just be
+     *  pre-concated with the current matrix.
+     */
+    virtual void drawPath(const SkDraw&, const SkPath& path,
+                          const SkPaint& paint,
+                          const SkMatrix* prePathMatrix = NULL,
+                          bool pathIsMutable = false);
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            const SkIRect* srcRectOrNull,
+                            const SkMatrix& matrix, const SkPaint& paint);
+    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                            int x, int y, const SkPaint& paint);
+    /**
+     *  Does not handle text decoration.
+     *  Decorations (underline and stike-thru) will be handled by SkCanvas.
+     */
+    virtual void drawText(const SkDraw&, const void* text, size_t len,
+                          SkScalar x, SkScalar y, const SkPaint& paint);
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int scalarsPerPos, const SkPaint& paint);
+    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint);
+#ifdef SK_BUILD_FOR_ANDROID
+    virtual void drawPosTextOnPath(const SkDraw& draw, const void* text, size_t len,
+                                   const SkPoint pos[], const SkPaint& paint,
+                                   const SkPath& path, const SkMatrix* matrix);
+#endif
+    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);
+    /** The SkDevice passed will be an SkDevice which was returned by a call to
+        onCreateCompatibleDevice on this device with kSaveLayer_Usage.
+     */
+    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+                            const SkPaint&);
+
+    /**
+     *  On success (returns true), copy the device pixels into the bitmap.
+     *  On failure, the bitmap parameter is left unchanged and false is
+     *  returned.
+     *
+     *  The device's pixels are converted to the bitmap's config. The only
+     *  supported config is kARGB_8888_Config, though this is likely to be
+     *  relaxed in  the future. The meaning of config kARGB_8888_Config is
+     *  modified by the enum param config8888. The default value interprets
+     *  kARGB_8888_Config as SkPMColor
+     *
+     *  If the bitmap has pixels already allocated, the device pixels will be
+     *  written there. If not, bitmap->allocPixels() will be called
+     *  automatically. If the bitmap is backed by a texture readPixels will
+     *  fail.
+     *
+     *  The actual pixels written is the intersection of the device's bounds,
+     *  and the rectangle formed by the bitmap's width,height and the specified
+     *  x,y. If bitmap pixels extend outside of that intersection, they will not
+     *  be modified.
+     *
+     *  Other failure conditions:
+     *    * If the device is not a raster device (e.g. PDF) then readPixels will
+     *      fail.
+     *    * If bitmap is texture-backed then readPixels will fail. (This may be
+     *      relaxed in the future.)
+     */
+    bool readPixels(SkBitmap* bitmap,
+                    int x, int y,
+                    SkCanvas::Config8888 config8888);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Update as needed the pixel value in the bitmap, so that the caller can
+        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 
+            maintained by the subclass.
+    */
+    virtual const SkBitmap& onAccessBitmap(SkBitmap*);
+
+    SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
+    // just for subclasses, to assign a custom pixelref
+    SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset) {
+        fBitmap.setPixelRef(pr, offset);
+        return pr;
+    }
+
+    /**
+     * Implements readPixels API. The caller will ensure that:
+     *  1. bitmap has pixel config kARGB_8888_Config.
+     *  2. bitmap has pixels.
+     *  3. The rectangle (x, y, x + bitmap->width(), y + bitmap->height()) is
+     *     contained in the device bounds.
+     */
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                              int x, int y,
+                              SkCanvas::Config8888 config8888);
+
+    /** Called when this device is installed into a Canvas. Balanaced by a call
+        to unlockPixels() when the device is removed from a Canvas.
+    */
+    virtual void lockPixels();
+    virtual void unlockPixels();
+
+    /**
+     *  Returns true if the device allows processing of this imagefilter. If
+     *  false is returned, then the filter is ignored. This may happen for
+     *  some subclasses that do not support pixel manipulations after drawing
+     *  has occurred (e.g. printing). The default implementation returns true.
+     */
+    virtual bool allowImageFilter(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.
+     */
+    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
+                             const SkMatrix& ctm,
+                             SkBitmap* result, SkIPoint* offset);
+
+    // This is equal kBGRA_Premul_Config8888 or kRGBA_Premul_Config8888 if
+    // either is identical to kNative_Premul_Config8888. Otherwise, -1.
+    static const SkCanvas::Config8888 kPMColorAlias;
+
+private:
+    friend class SkCanvas;
+    friend struct DeviceCM; //for setMatrixClip
+    friend class SkDraw;
+    friend class SkDrawIter;
+    friend class SkDeviceFilteredPaint;
+    friend class DeviceImageFilterProxy;
+
+    // just called by SkCanvas when built as a layer
+    void setOrigin(int x, int y) { fOrigin.set(x, y); }
+    // just called by SkCanvas for saveLayer
+    SkDevice* createCompatibleDeviceForSaveLayer(SkBitmap::Config config,
+                                                 int width, int height,
+                                                 bool isOpaque);
+
+    /**
+     * Subclasses should override this to implement createCompatibleDevice.
+     */
+    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
+                                               int width, int height,
+                                               bool isOpaque,
+                                               Usage usage);
+
+    /** Causes any deferred drawing to the device to be completed.
+     */
+    virtual void flush() {}
+
+    SkBitmap    fBitmap;
+    SkIPoint    fOrigin;
+    SkMetaData* fMetaData;
+};
+
+#endif
diff --git a/include/core/SkDeviceProfile.h b/legacy/include/core/SkDeviceProfile.h
similarity index 100%
rename from include/core/SkDeviceProfile.h
rename to legacy/include/core/SkDeviceProfile.h
diff --git a/legacy/include/core/SkDither.h b/legacy/include/core/SkDither.h
new file mode 100644
index 0000000..692c7e4
--- /dev/null
+++ b/legacy/include/core/SkDither.h
@@ -0,0 +1,198 @@
+
+/*
+ * 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 SkDither_DEFINED
+#define SkDither_DEFINED
+
+#include "SkColorPriv.h"
+
+#define SK_DitherValueMax4444   15
+#define SK_DitherValueMax565    7
+
+/*  need to use macros for bit-counts for each component, and then
+    move these into SkColorPriv.h
+*/
+
+#define SkDITHER_R32_FOR_565_MACRO(r, d)    (r + d - (r >> 5))
+#define SkDITHER_G32_FOR_565_MACRO(g, d)    (g + (d >> 1) - (g >> 6))
+#define SkDITHER_B32_FOR_565_MACRO(b, d)    (b + d - (b >> 5))
+
+#define SkDITHER_A32_FOR_4444_MACRO(a, d)    (a + 15 - (a >> 4))
+#define SkDITHER_R32_FOR_4444_MACRO(r, d)    (r + d - (r >> 4))
+#define SkDITHER_G32_FOR_4444_MACRO(g, d)    (g + d - (g >> 4))
+#define SkDITHER_B32_FOR_4444_MACRO(b, d)    (b + d - (b >> 4))
+
+#ifdef SK_DEBUG
+    inline unsigned SkDITHER_R32_FOR_565(unsigned r, unsigned d)
+    {
+        SkASSERT(d <= SK_DitherValueMax565);
+        SkA32Assert(r);
+        r = SkDITHER_R32_FOR_565_MACRO(r, d);
+        SkA32Assert(r);
+        return r;
+    }
+    inline unsigned SkDITHER_G32_FOR_565(unsigned g, unsigned d)
+    {
+        SkASSERT(d <= SK_DitherValueMax565);
+        SkG32Assert(g);
+        g = SkDITHER_G32_FOR_565_MACRO(g, d);
+        SkG32Assert(g);
+        return g;
+    }
+    inline unsigned SkDITHER_B32_FOR_565(unsigned b, unsigned d)
+    {
+        SkASSERT(d <= SK_DitherValueMax565);
+        SkB32Assert(b);
+        b = SkDITHER_B32_FOR_565_MACRO(b, d);
+        SkB32Assert(b);
+        return b;
+    }
+#else
+    #define SkDITHER_R32_FOR_565(r, d)  SkDITHER_R32_FOR_565_MACRO(r, d)
+    #define SkDITHER_G32_FOR_565(g, d)  SkDITHER_G32_FOR_565_MACRO(g, d)
+    #define SkDITHER_B32_FOR_565(b, d)  SkDITHER_B32_FOR_565_MACRO(b, d)
+#endif
+
+#define SkDITHER_R32To565(r, d)  SkR32ToR16(SkDITHER_R32_FOR_565(r, d))
+#define SkDITHER_G32To565(g, d)  SkG32ToG16(SkDITHER_G32_FOR_565(g, d))
+#define SkDITHER_B32To565(b, d)  SkB32ToB16(SkDITHER_B32_FOR_565(b, d))
+
+#define SkDITHER_A32To4444(a, d)  SkA32To4444(SkDITHER_A32_FOR_4444_MACRO(a, d))
+#define SkDITHER_R32To4444(r, d)  SkR32To4444(SkDITHER_R32_FOR_4444_MACRO(r, d))
+#define SkDITHER_G32To4444(g, d)  SkG32To4444(SkDITHER_G32_FOR_4444_MACRO(g, d))
+#define SkDITHER_B32To4444(b, d)  SkB32To4444(SkDITHER_B32_FOR_4444_MACRO(b, d))
+
+static inline SkPMColor SkDitherARGB32For565(SkPMColor c, unsigned dither)
+{
+    SkASSERT(dither <= SK_DitherValueMax565);
+    
+    unsigned sa = SkGetPackedA32(c);
+    dither = SkAlphaMul(dither, SkAlpha255To256(sa));
+
+    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(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);
+}
+
+static inline uint16_t SkDitherRGBTo565(U8CPU r, U8CPU g, U8CPU b,
+                                              unsigned dither)
+{
+    SkASSERT(dither <= SK_DitherValueMax565);
+    r = SkDITHER_R32To565(r, dither);
+    g = SkDITHER_G32To565(g, dither);
+    b = SkDITHER_B32To565(b, dither);
+    return SkPackRGB16(r, g, b);
+}
+
+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);    
+    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);
+}
+
+///////////////////////// 4444
+
+static inline SkPMColor16 SkDitherARGB32To4444(U8CPU a, U8CPU r, U8CPU g,
+                                               U8CPU b, unsigned dither)
+{
+    dither = SkAlphaMul(dither, SkAlpha255To256(a));
+
+    a = SkDITHER_A32To4444(a, dither);
+    r = SkDITHER_R32To4444(r, dither);
+    g = SkDITHER_G32To4444(g, dither);
+    b = SkDITHER_B32To4444(b, dither);
+    
+    return SkPackARGB4444(a, r, g, b);
+}
+
+static inline SkPMColor16 SkDitherARGB32To4444(SkPMColor c, unsigned dither)
+{
+    unsigned a = SkGetPackedA32(c);
+    unsigned r = SkGetPackedR32(c);
+    unsigned g = SkGetPackedG32(c);
+    unsigned b = SkGetPackedB32(c);
+
+    dither = SkAlphaMul(dither, SkAlpha255To256(a));
+
+    a = SkDITHER_A32To4444(a, dither);
+    r = SkDITHER_R32To4444(r, dither);
+    g = SkDITHER_G32To4444(g, dither);
+    b = SkDITHER_B32To4444(b, dither);
+    
+    return SkPackARGB4444(a, r, g, b);
+}
+
+// TODO: need dither routines for 565 -> 4444
+
+// this toggles between a 4x4 and a 1x4 array
+//#define ENABLE_DITHER_MATRIX_4X4
+
+#ifdef ENABLE_DITHER_MATRIX_4X4
+    extern const uint8_t gDitherMatrix_4Bit_4X4[4][4];
+    extern const uint8_t gDitherMatrix_3Bit_4X4[4][4];
+
+    #define DITHER_4444_SCAN(y) const uint8_t* dither_scan = gDitherMatrix_4Bit_4X4[(y) & 3]
+    #define DITHER_565_SCAN(y)  const uint8_t* dither_scan = gDitherMatrix_3Bit_4X4[(y) & 3]
+
+    #define DITHER_VALUE(x) dither_scan[(x) & 3]
+#else
+    extern const uint16_t gDitherMatrix_4Bit_16[4];
+    extern const uint16_t gDitherMatrix_3Bit_16[4];
+
+    #define DITHER_4444_SCAN(y) const uint16_t dither_scan = gDitherMatrix_4Bit_16[(y) & 3]
+    #define DITHER_565_SCAN(y)  const uint16_t dither_scan = gDitherMatrix_3Bit_16[(y) & 3]
+
+    #define DITHER_VALUE(x) ((dither_scan >> (((x) & 3) << 2)) & 0xF)
+#endif
+
+#define DITHER_INC_X(x) ++(x)
+
+#endif
diff --git a/legacy/include/core/SkDraw.h b/legacy/include/core/SkDraw.h
new file mode 100644
index 0000000..8c659c2
--- /dev/null
+++ b/legacy/include/core/SkDraw.h
@@ -0,0 +1,157 @@
+
+/*
+ * 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 SkDraw_DEFINED
+#define SkDraw_DEFINED
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkMask.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkRect.h"
+#include "SkAutoKern.h"
+
+class SkBounder;
+class SkClipStack;
+class SkDevice;
+class SkPath;
+class SkRegion;
+class SkRasterClip;
+struct SkDrawProcs;
+
+class SkDraw {
+public:
+    SkDraw();
+    SkDraw(const SkDraw& src);
+
+    void    drawPaint(const SkPaint&) const;
+    void    drawPoints(SkCanvas::PointMode, size_t count, const SkPoint[],
+                       const SkPaint&, bool forceUseDevice = false) const;
+    void    drawRect(const SkRect&, const SkPaint&) const;
+    /**
+     *  To save on mallocs, we allow a flag that tells us that srcPath is
+     *  mutable, so that we don't have to make copies of it as we transform it.
+     *
+     *  If prePathMatrix is not null, it should logically be applied before any
+     *  stroking or other effects. If there are no effects on the paint that
+     *  affect the geometry/rasterization, then the pre matrix can just be
+     *  pre-concated with the current matrix.
+     */
+    void    drawPath(const SkPath& srcPath, const SkPaint&,
+                     const SkMatrix* prePathMatrix, bool pathIsMutable) const;
+    void    drawBitmap(const SkBitmap&, const SkMatrix&, const SkPaint&) const;
+    void    drawSprite(const SkBitmap&, int x, int y, const SkPaint&) const;
+    void    drawText(const char text[], size_t byteLength, SkScalar x,
+                     SkScalar y, const SkPaint& paint) const;
+    void    drawPosText(const char text[], size_t byteLength,
+                        const SkScalar pos[], SkScalar constY,
+                        int scalarsPerPosition, const SkPaint& paint) const;
+    void    drawTextOnPath(const char text[], size_t byteLength,
+                        const SkPath&, const SkMatrix*, const SkPaint&) const;
+#ifdef SK_BUILD_FOR_ANDROID
+    void    drawPosTextOnPath(const char text[], size_t byteLength,
+                              const SkPoint pos[], const SkPaint& paint,
+                              const SkPath& path, const SkMatrix* matrix) const;
+#endif
+    void    drawVertices(SkCanvas::VertexMode mode, int count,
+                         const SkPoint vertices[], const SkPoint textures[],
+                         const SkColor colors[], SkXfermode* xmode,
+                         const uint16_t indices[], int ptCount,
+                         const SkPaint& paint) const;
+
+    void drawPath(const SkPath& src, const SkPaint& paint) const {
+        this->drawPath(src, paint, NULL, false);
+    }
+
+    /** Helper function that creates a mask from a path and an optional maskfilter.
+        Note however, that the resulting mask will not have been actually filtered,
+        that must be done afterwards (by calling filterMask). The maskfilter is provided
+        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);
+
+    enum RectType {
+        kHair_RectType,
+        kFill_RectType,
+        kStroke_RectType,
+        kPath_RectType
+    };
+
+    /**
+     *  Based on the paint's style, strokeWidth, and the matrix, classify how
+     *  to draw the rect. If no special-case is available, returns
+     *  kPath_RectType.
+     *
+     *  Iff RectType == kStroke_RectType, then strokeSize is set to the device
+     *  width and height of the stroke.
+     */
+    static RectType ComputeRectType(const SkPaint&, const SkMatrix&,
+                                    SkPoint* strokeSize);
+
+private:
+    void    drawText_asPaths(const char text[], size_t byteLength,
+                             SkScalar x, SkScalar y, const SkPaint&) const;
+    void    drawDevMask(const SkMask& mask, const SkPaint&) const;
+    void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
+
+public:
+    const SkBitmap* fBitmap;        // required
+    const SkMatrix* fMatrix;        // required
+    const SkRegion* fClip;          // DEPRECATED
+    const SkRasterClip* fRC;        // required
+
+    const SkClipStack* fClipStack;  // optional
+    SkDevice*       fDevice;        // optional
+    SkBounder*      fBounder;       // optional
+    SkDrawProcs*    fProcs;         // optional
+
+    const SkMatrix* fMVMatrix;      // optional
+    const SkMatrix* fExtMatrix;     // optional
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#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/legacy/include/core/SkDrawFilter.h b/legacy/include/core/SkDrawFilter.h
new file mode 100644
index 0000000..303b80e
--- /dev/null
+++ b/legacy/include/core/SkDrawFilter.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 SkDrawFilter_DEFINED
+#define SkDrawFilter_DEFINED
+
+#include "SkRefCnt.h"
+
+class SkCanvas;
+class SkPaint;
+
+/**
+ *  Right before something is being draw, filter() is called with the
+ *  paint. The filter may modify the paint as it wishes, which will then be
+ *  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 {
+public:
+    enum Type {
+        kPaint_Type,
+        kPoint_Type,
+        kLine_Type,
+        kBitmap_Type,
+        kRect_Type,
+        kPath_Type,
+        kText_Type
+    };
+
+    /**
+     *  Called with the paint that will be used to draw the specified type.
+     *  The implementation may modify the paint as they wish.
+     */
+    virtual void filter(SkPaint*, Type) = 0;
+};
+
+#endif
diff --git a/legacy/include/core/SkDrawLooper.h b/legacy/include/core/SkDrawLooper.h
new file mode 100644
index 0000000..e8265db
--- /dev/null
+++ b/legacy/include/core/SkDrawLooper.h
@@ -0,0 +1,70 @@
+
+/*
+ * 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 SkDrawLooper_DEFINED
+#define SkDrawLooper_DEFINED
+
+#include "SkFlattenable.h"
+
+class SkCanvas;
+class SkPaint;
+
+/** \class SkDrawLooper
+    Subclasses of SkDrawLooper can be attached to a SkPaint. Where they are,
+    and something is drawn to a canvas with that paint, the looper subclass will
+    be called, allowing it to modify the canvas and/or paint for that draw call.
+    More than that, via the next() method, the looper can modify the draw to be
+    invoked multiple times (hence the name loop-er), allow it to perform effects
+    like shadows or frame/fills, that require more than one pass.
+*/
+class SK_API SkDrawLooper : public SkFlattenable {
+public:
+    /**
+     *  Called right before something is being drawn. This will be followed by
+     *  calls to next() until next() returns false.
+     */
+    virtual void init(SkCanvas*) = 0;
+
+    /**
+     *  Called in a loop (after init()). Each time true is returned, the object
+     *  is drawn (possibly with a modified canvas and/or paint). When false is
+     *  finally returned, drawing for the object stops.
+     *
+     *  On each call, the paint will be in its original state, but the canvas
+     *  will be as it was following the previous call to next() or init().
+     *
+     *  The implementation must ensure that, when next() finally returns false,
+     *  that the canvas has been restored to the state it was initially, before
+     *  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
+     * return true for the canComputeFastBounds() function.  If that function
+     * returns false then computeFastBounds behavior is undefined otherwise it
+     * is expected to have the following behavior. Given the parent paint and
+     * the parent's bounding rect the subclass must fill in and return the
+     * storage rect, where the storage rect is with the union of the src rect
+     * and the looper's bounding rect.
+     */
+    virtual bool canComputeFastBounds(const SkPaint& paint);
+    virtual void computeFastBounds(const SkPaint& paint,
+                                   const SkRect& src, SkRect* dst);
+
+protected:
+    SkDrawLooper() {}
+    SkDrawLooper(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkEdgeClipper.h b/legacy/include/core/SkEdgeClipper.h
similarity index 100%
rename from include/core/SkEdgeClipper.h
rename to legacy/include/core/SkEdgeClipper.h
diff --git a/legacy/include/core/SkEmptyShader.h b/legacy/include/core/SkEmptyShader.h
new file mode 100644
index 0000000..bf270bf
--- /dev/null
+++ b/legacy/include/core/SkEmptyShader.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 SkEmptyShader_DEFINED
+#define SkEmptyShader_DEFINED
+
+#include "SkShader.h"
+
+/**
+ *  \class SkEmptyShader
+ *  A Shader that always draws nothing. Its setContext always returns false,
+ *  so it never expects that its shadeSpan() methods will get called.
+ */
+class SK_API SkEmptyShader : public SkShader {
+public:
+    SkEmptyShader() {}
+
+    virtual uint32_t getFlags() SK_OVERRIDE;
+    virtual uint8_t getSpan16Alpha() const SK_OVERRIDE;
+    virtual bool setContext(const SkBitmap&, const SkPaint&,
+                            const SkMatrix&) SK_OVERRIDE;
+    virtual void shadeSpan(int x, int y, SkPMColor span[], int count) SK_OVERRIDE;
+    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&);
+
+    virtual Factory getFactory() SK_OVERRIDE;
+    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+
+private:
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/legacy/include/core/SkEndian.h b/legacy/include/core/SkEndian.h
new file mode 100644
index 0000000..910cf1e
--- /dev/null
+++ b/legacy/include/core/SkEndian.h
@@ -0,0 +1,152 @@
+
+/*
+ * 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 SkEndian_DEFINED
+#define SkEndian_DEFINED
+
+#include "SkTypes.h"
+
+/** \file SkEndian.h
+
+    Macros and helper functions for handling 16 and 32 bit values in
+    big and little endian formats.
+*/
+
+#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN)
+    #error "can't have both LENDIAN and BENDIAN defined"
+#endif
+
+#if !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN)
+    #error "need either LENDIAN or BENDIAN defined"
+#endif
+
+/** Swap the two bytes in the low 16bits of the parameters.
+    e.g. 0x1234 -> 0x3412
+*/
+static inline uint16_t SkEndianSwap16(U16CPU value) {
+    SkASSERT(value == (uint16_t)value);
+    return static_cast<uint16_t>((value >> 8) | (value << 8));
+}
+template<uint16_t N> struct SkTEndianSwap16 {
+    static const uint16_t value = static_cast<uint16_t>((N >> 8) | ((N & 0xFF) << 8));
+};
+
+/** Vector version of SkEndianSwap16(), which swaps the
+    low two bytes of each value in the array.
+*/
+static inline void SkEndianSwap16s(uint16_t array[], int count) {
+    SkASSERT(count == 0 || array != NULL);
+
+    while (--count >= 0) {
+        *array = SkEndianSwap16(*array);
+        array += 1;
+    }
+}
+
+/** Reverse all 4 bytes in a 32bit value.
+    e.g. 0x12345678 -> 0x78563412
+*/
+static inline uint32_t SkEndianSwap32(uint32_t value) {
+    return  ((value & 0xFF) << 24) |
+            ((value & 0xFF00) << 8) |
+            ((value & 0xFF0000) >> 8) |
+            (value >> 24);
+}
+template<uint32_t N> struct SkTEndianSwap32 {
+    static const uint32_t value = ((N & 0xFF) << 24) |
+                                  ((N & 0xFF00) << 8) |
+                                  ((N & 0xFF0000) >> 8) |
+                                  (N >> 24);
+};
+
+/** Vector version of SkEndianSwap16(), which swaps the
+    bytes of each value in the array.
+*/
+static inline void SkEndianSwap32s(uint32_t array[], int count) {
+    SkASSERT(count == 0 || array != NULL);
+
+    while (--count >= 0) {
+        *array = SkEndianSwap32(*array);
+        array += 1;
+    }
+}
+
+#ifdef SK_CPU_LENDIAN
+    #define SkEndian_SwapBE16(n)    SkEndianSwap16(n)
+    #define SkEndian_SwapBE32(n)    SkEndianSwap32(n)
+    #define SkEndian_SwapLE16(n)    (n)
+    #define SkEndian_SwapLE32(n)    (n)
+
+    #define SkTEndian_SwapBE16(n)    SkTEndianSwap16<n>::value
+    #define SkTEndian_SwapBE32(n)    SkTEndianSwap32<n>::value
+    #define SkTEndian_SwapLE16(n)    (n)
+    #define SkTEndian_SwapLE32(n)    (n)
+#else   // SK_CPU_BENDIAN
+    #define SkEndian_SwapBE16(n)    (n)
+    #define SkEndian_SwapBE32(n)    (n)
+    #define SkEndian_SwapLE16(n)    SkEndianSwap16(n)
+    #define SkEndian_SwapLE32(n)    SkEndianSwap32(n)
+
+    #define SkTEndian_SwapBE16(n)    (n)
+    #define SkTEndian_SwapBE32(n)    (n)
+    #define SkTEndian_SwapLE16(n)    SkTEndianSwap16<n>::value
+    #define SkTEndian_SwapLE32(n)    SkTEndianSwap32<n>::value
+#endif
+
+// When a bytestream is embedded in a 32-bit word, how far we need to
+// shift the word to extract each byte from the low 8 bits by anding with 0xff.
+#ifdef SK_CPU_LENDIAN
+    #define SkEndian_Byte0Shift 0
+    #define SkEndian_Byte1Shift 8
+    #define SkEndian_Byte2Shift 16
+    #define SkEndian_Byte3Shift 24
+#else   // SK_CPU_BENDIAN
+    #define SkEndian_Byte0Shift 24
+    #define SkEndian_Byte1Shift 16
+    #define SkEndian_Byte2Shift 8
+    #define SkEndian_Byte3Shift 0
+#endif
+
+
+#if defined(SK_UINT8_BITFIELD_LENDIAN) && defined(SK_UINT8_BITFIELD_BENDIAN)
+    #error "can't have both bitfield LENDIAN and BENDIAN defined"
+#endif
+
+#if !defined(SK_UINT8_BITFIELD_LENDIAN) && !defined(SK_UINT8_BITFIELD_BENDIAN)
+    #ifdef SK_CPU_LENDIAN
+        #define SK_UINT8_BITFIELD_LENDIAN
+    #else
+        #define SK_UINT8_BITFIELD_BENDIAN
+    #endif
+#endif
+
+#ifdef SK_UINT8_BITFIELD_LENDIAN
+    #define SK_UINT8_BITFIELD(f0, f1, f2, f3, f4, f5, f6, f7) \
+        SK_OT_BYTE f0 : 1; \
+        SK_OT_BYTE f1 : 1; \
+        SK_OT_BYTE f2 : 1; \
+        SK_OT_BYTE f3 : 1; \
+        SK_OT_BYTE f4 : 1; \
+        SK_OT_BYTE f5 : 1; \
+        SK_OT_BYTE f6 : 1; \
+        SK_OT_BYTE f7 : 1;
+#else
+    #define SK_UINT8_BITFIELD(f0, f1, f2, f3, f4, f5, f6, f7) \
+        SK_OT_BYTE f7 : 1; \
+        SK_OT_BYTE f6 : 1; \
+        SK_OT_BYTE f5 : 1; \
+        SK_OT_BYTE f4 : 1; \
+        SK_OT_BYTE f3 : 1; \
+        SK_OT_BYTE f2 : 1; \
+        SK_OT_BYTE f1 : 1; \
+        SK_OT_BYTE f0 : 1;
+#endif
+
+#endif
+
diff --git a/include/core/SkFDot6.h b/legacy/include/core/SkFDot6.h
similarity index 100%
rename from include/core/SkFDot6.h
rename to legacy/include/core/SkFDot6.h
diff --git a/legacy/include/core/SkFixed.h b/legacy/include/core/SkFixed.h
new file mode 100644
index 0000000..0af5d9d
--- /dev/null
+++ b/legacy/include/core/SkFixed.h
@@ -0,0 +1,267 @@
+
+/*
+ * 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 SkFixed_DEFINED
+#define SkFixed_DEFINED
+
+#include "SkTypes.h"
+
+/** \file SkFixed.h
+
+    Types and macros for 16.16 fixed point
+*/
+
+/** 32 bit signed integer used to represent fractions values with 16 bits to the right of the decimal point
+*/
+typedef int32_t             SkFixed;
+#define SK_Fixed1           (1 << 16)
+#define SK_FixedHalf        (1 << 15)
+#define SK_FixedMax         (0x7FFFFFFF)
+#define SK_FixedMin         (-SK_FixedMax)
+#define SK_FixedNaN         ((int) 0x80000000)
+#define SK_FixedPI          (0x3243F)
+#define SK_FixedSqrt2       (92682)
+#define SK_FixedTanPIOver8  (0x6A0A)
+#define SK_FixedRoot2Over2  (0xB505)
+
+#ifdef SK_CAN_USE_FLOAT
+    #define SkFixedToFloat(x)   ((x) * 1.5258789e-5f)
+#if 1
+    #define SkFloatToFixed(x)   ((SkFixed)((x) * SK_Fixed1))
+#else
+    // pins over/under flows to max/min int32 (slower than just a cast)
+    static inline SkFixed SkFloatToFixed(float x) {
+        int64_t n = x * SK_Fixed1;
+        return (SkFixed)n;
+    }
+#endif
+
+    #define SkFixedToDouble(x)  ((x) * 1.5258789e-5)
+    #define SkDoubleToFixed(x)  ((SkFixed)((x) * SK_Fixed1))
+#endif
+
+/** 32 bit signed integer used to represent fractions values with 30 bits to the right of the decimal point
+*/
+typedef int32_t             SkFract;
+#define SK_Fract1           (1 << 30)
+#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
+
+/** Converts an integer to a SkFixed, asserting that the result does not overflow
+    a 32 bit signed integer
+*/
+#ifdef SK_DEBUG
+    inline SkFixed SkIntToFixed(int n)
+    {
+        SkASSERT(n >= -32768 && n <= 32767);
+        return n << 16;
+    }
+#else
+    //  force the cast to SkFixed to ensure that the answer is signed (like the debug version)
+    #define SkIntToFixed(n)     (SkFixed)((n) << 16)
+#endif
+
+/** Converts a SkFixed to a SkFract, asserting that the result does not overflow
+    a 32 bit signed integer
+*/
+#ifdef SK_DEBUG
+    inline SkFract SkFixedToFract(SkFixed x)
+    {
+        SkASSERT(x >= (-2 << 16) && x <= (2 << 16) - 1);
+        return x << 14;
+    }
+#else
+    #define SkFixedToFract(x)   ((x) << 14)
+#endif
+
+/** Returns the signed fraction of a SkFixed
+*/
+inline SkFixed SkFixedFraction(SkFixed x)
+{
+    SkFixed mask = x >> 31 << 16;
+    return (x & 0xFFFF) | mask;
+}
+
+/** Converts a SkFract to a SkFixed
+*/
+#define SkFractToFixed(x)   ((x) >> 14)
+
+#define SkFixedRoundToInt(x)    (((x) + SK_FixedHalf) >> 16)
+#define SkFixedCeilToInt(x)     (((x) + SK_Fixed1 - 1) >> 16)
+#define SkFixedFloorToInt(x)    ((x) >> 16)
+
+#define SkFixedRoundToFixed(x)  (((x) + SK_FixedHalf) & 0xFFFF0000)
+#define SkFixedCeilToFixed(x)   (((x) + SK_Fixed1 - 1) & 0xFFFF0000)
+#define SkFixedFloorToFixed(x)  ((x) & 0xFFFF0000)
+
+// DEPRECATED
+#define SkFixedFloor(x)     SkFixedFloorToInt(x)
+#define SkFixedCeil(x)      SkFixedCeilToInt(x)
+#define SkFixedRound(x)     SkFixedRoundToInt(x)
+
+#define SkFixedAbs(x)       SkAbs32(x)
+#define SkFixedAve(a, b)    (((a) + (b)) >> 1)
+
+SkFixed SkFixedMul_portable(SkFixed, SkFixed);
+SkFract SkFractMul_portable(SkFract, SkFract);
+inline SkFixed SkFixedSquare_portable(SkFixed value)
+{
+    uint32_t a = SkAbs32(value);
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    SkFixed result = ah * a + al * ah + (al * al >> 16);
+    if (result >= 0)
+        return result;
+    else // Overflow.
+        return SK_FixedMax;
+}
+
+#define SkFixedDiv(numer, denom)    SkDivBits(numer, denom, 16)
+SkFixed SkFixedDivInt(int32_t numer, int32_t denom);
+SkFixed SkFixedMod(SkFixed numer, SkFixed denom);
+#define SkFixedInvert(n)            SkDivBits(SK_Fixed1, n, 16)
+SkFixed SkFixedFastInvert(SkFixed n);
+#define SkFixedSqrt(n)              SkSqrtBits(n, 23)
+SkFixed SkFixedMean(SkFixed a, SkFixed b);  //*< returns sqrt(x*y)
+int SkFixedMulCommon(SkFixed, int , int bias);  // internal used by SkFixedMulFloor, SkFixedMulCeil, SkFixedMulRound
+
+#define SkFractDiv(numer, denom)    SkDivBits(numer, denom, 30)
+#define SkFractSqrt(n)              SkSqrtBits(n, 30)
+
+SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValueOrNull);
+#define SkFixedSin(radians)         SkFixedSinCos(radians, NULL)
+inline SkFixed SkFixedCos(SkFixed radians)
+{
+    SkFixed cosValue;
+    (void)SkFixedSinCos(radians, &cosValue);
+    return cosValue;
+}
+SkFixed SkFixedTan(SkFixed radians);
+SkFixed SkFixedASin(SkFixed);
+SkFixed SkFixedACos(SkFixed);
+SkFixed SkFixedATan2(SkFixed y, SkFixed x);
+SkFixed SkFixedExp(SkFixed);
+SkFixed SkFixedLog(SkFixed);
+
+#define SK_FixedNearlyZero          (SK_Fixed1 >> 12)
+
+inline bool SkFixedNearlyZero(SkFixed x, SkFixed tolerance = SK_FixedNearlyZero)
+{
+    SkASSERT(tolerance > 0);
+    return SkAbs32(x) < tolerance;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+// Now look for ASM overrides for our portable versions (should consider putting this in its own file)
+
+#ifdef SkLONGLONG
+    inline SkFixed SkFixedMul_longlong(SkFixed a, SkFixed b)
+    {
+        return (SkFixed)((SkLONGLONG)a * b >> 16);
+    }
+    inline SkFract SkFractMul_longlong(SkFract a, SkFract b)
+    {
+        return (SkFixed)((SkLONGLONG)a * b >> 30);
+    }
+    inline SkFixed SkFixedSquare_longlong(SkFixed value)
+    {
+        return (SkFixed)((SkLONGLONG)value * value >> 16);
+    }
+    #define SkFixedMul(a,b)     SkFixedMul_longlong(a,b)
+    #define SkFractMul(a,b)     SkFractMul_longlong(a,b)
+    #define SkFixedSquare(a)    SkFixedSquare_longlong(a)
+#endif
+
+#if defined(__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
+    */
+    inline SkFixed SkFloatToFixed_arm(float x)
+    {
+        register int32_t y, z;
+        asm("movs    %1, %3, lsl #1         \n"
+            "mov     %2, #0x8E              \n"
+            "sub     %1, %2, %1, lsr #24    \n"
+            "mov     %2, %3, lsl #8         \n"
+            "orr     %2, %2, #0x80000000    \n"
+            "mov     %1, %2, lsr %1         \n"
+            "rsbcs   %1, %1, #0             \n"
+            : "=r"(x), "=&r"(y), "=&r"(z)
+            : "r"(x)
+            : "cc"
+            );
+        return y;
+    }
+    inline SkFixed SkFixedMul_arm(SkFixed x, SkFixed y)
+    {
+        register int32_t t;
+        asm("smull  %0, %2, %1, %3          \n"
+            "mov    %0, %0, lsr #16         \n"
+            "orr    %0, %0, %2, lsl #16     \n"
+            : "=r"(x), "=&r"(y), "=r"(t)
+            : "r"(x), "1"(y)
+            :
+            );
+        return x;
+    }
+    inline SkFixed SkFixedMulAdd_arm(SkFixed x, SkFixed y, SkFixed a)
+    {
+        register int32_t t;
+        asm("smull  %0, %3, %1, %4          \n"
+            "add    %0, %2, %0, lsr #16     \n"
+            "add    %0, %0, %3, lsl #16     \n"
+            : "=r"(x), "=&r"(y), "=&r"(a), "=r"(t)
+            : "%r"(x), "1"(y), "2"(a)
+            :
+            );
+        return x;
+    }
+    inline SkFixed SkFractMul_arm(SkFixed x, SkFixed y)
+    {
+        register int32_t t;
+        asm("smull  %0, %2, %1, %3          \n"
+            "mov    %0, %0, lsr #30         \n"
+            "orr    %0, %0, %2, lsl #2      \n"
+            : "=r"(x), "=&r"(y), "=r"(t)
+            : "r"(x), "1"(y)
+            :
+            );
+        return x;
+    }
+    #undef SkFixedMul
+    #undef SkFractMul
+    #define SkFixedMul(x, y)        SkFixedMul_arm(x, y)
+    #define SkFractMul(x, y)        SkFractMul_arm(x, y)
+    #define SkFixedMulAdd(x, y, a)  SkFixedMulAdd_arm(x, y, a)
+
+    #undef SkFloatToFixed
+    #define SkFloatToFixed(x)  SkFloatToFixed_arm(x)
+#endif
+
+/////////////////////// Now define our macros to the portable versions if they weren't overridden
+
+#ifndef SkFixedSquare
+    #define SkFixedSquare(x)    SkFixedSquare_portable(x)
+#endif
+#ifndef SkFixedMul
+    #define SkFixedMul(x, y)    SkFixedMul_portable(x, y)
+#endif
+#ifndef SkFractMul
+    #define SkFractMul(x, y)    SkFractMul_portable(x, y)
+#endif
+#ifndef SkFixedMulAdd
+    #define SkFixedMulAdd(x, y, a)  (SkFixedMul(x, y) + (a))
+#endif
+
+#endif
diff --git a/legacy/include/core/SkFlate.h b/legacy/include/core/SkFlate.h
new file mode 100644
index 0000000..c111de0
--- /dev/null
+++ b/legacy/include/core/SkFlate.h
@@ -0,0 +1,52 @@
+
+/*
+ * 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 SkFlate_DEFINED
+#define SkFlate_DEFINED
+
+#include "SkTypes.h"
+
+class SkData;
+class SkWStream;
+class SkStream;
+
+/** \class SkFlate
+    A class to provide access to the flate compression algorithm.
+*/
+class SkFlate {
+public:
+    /** Indicates if the flate algorithm is available.
+     */
+    static bool HaveFlate();
+
+    /**
+     *  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(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.
+     */
+    static bool Inflate(SkStream* src, SkWStream* dst);
+};
+
+#endif
diff --git a/legacy/include/core/SkFlattenable.h b/legacy/include/core/SkFlattenable.h
new file mode 100644
index 0000000..34ca34e
--- /dev/null
+++ b/legacy/include/core/SkFlattenable.h
@@ -0,0 +1,257 @@
+
+/*
+ * 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 SkFlattenable_DEFINED
+#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_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END \
+    }
+
+#endif
+
+/** \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:
+    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&) {}
+
+private:
+#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+    static void InitializeFlattenables();
+#endif
+
+    friend class SkGraphics;
+};
+
+// 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;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkFloatBits.h b/legacy/include/core/SkFloatBits.h
new file mode 100644
index 0000000..e6fc5b5
--- /dev/null
+++ b/legacy/include/core/SkFloatBits.h
@@ -0,0 +1,143 @@
+
+/*
+ * 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 SkFloatBits_DEFINED
+#define SkFloatBits_DEFINED
+
+#include "SkTypes.h"
+
+/** Convert a sign-bit int (i.e. float interpreted as int) into a 2s compliement
+    int. This also converts -0 (0x80000000) to 0. Doing this to a float allows
+    it to be compared using normal C operators (<, <=, etc.)
+*/
+static inline int32_t SkSignBitTo2sCompliment(int32_t x) {
+    if (x < 0) {
+        x &= 0x7FFFFFFF;
+        x = -x;
+    }
+    return x;
+}
+
+/** Convert a 2s compliment int to a sign-bit (i.e. int interpreted as float).
+    This undoes the result of SkSignBitTo2sCompliment().
+ */
+static inline int32_t Sk2sComplimentToSignBit(int32_t x) {
+    int sign = x >> 31;
+    // make x positive
+    x = (x ^ sign) - sign;
+    // set the sign bit as needed
+    x |= sign << 31;
+    return x;
+}
+
+/** Given the bit representation of a float, return its value cast to an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+*/
+int32_t SkFloatBits_toIntCast(int32_t floatBits);
+
+/** Given the bit representation of a float, return its floor as an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+ */
+SK_API int32_t SkFloatBits_toIntFloor(int32_t floatBits);
+
+/** Given the bit representation of a float, return it rounded to an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+ */
+SK_API int32_t SkFloatBits_toIntRound(int32_t floatBits);
+
+/** Given the bit representation of a float, return its ceiling as an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+ */
+SK_API int32_t SkFloatBits_toIntCeil(int32_t floatBits);
+
+
+#ifdef SK_CAN_USE_FLOAT
+
+union SkFloatIntUnion {
+    float   fFloat;
+    int32_t fSignBitInt;
+};
+
+// Helper to see a float as its bit pattern (w/o aliasing warnings)
+static inline int32_t SkFloat2Bits(float x) {
+    SkFloatIntUnion data;
+    data.fFloat = x;
+    return data.fSignBitInt;
+}
+
+// Helper to see a bit pattern as a float (w/o aliasing warnings)
+static inline float SkBits2Float(int32_t floatAsBits) {
+    SkFloatIntUnion data;
+    data.fSignBitInt = floatAsBits;
+    return data.fFloat;
+}
+
+/** Return the float as a 2s compliment int. Just to be used to compare floats
+    to each other or against positive float-bit-constants (like 0). This does
+    not return the int equivalent of the float, just something cheaper for
+    compares-only.
+ */
+static inline int32_t SkFloatAs2sCompliment(float x) {
+    return SkSignBitTo2sCompliment(SkFloat2Bits(x));
+}
+
+/** Return the 2s compliment int as a float. This undos the result of
+    SkFloatAs2sCompliment
+ */
+static inline float Sk2sComplimentAsFloat(int32_t x) {
+    return SkBits2Float(Sk2sComplimentToSignBit(x));
+}
+
+/** Return x cast to a float (i.e. (float)x)
+*/
+float SkIntToFloatCast(int x);
+float SkIntToFloatCast_NoOverflowCheck(int x);
+
+/** Return the float cast to an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntCast(float x) {
+    return SkFloatBits_toIntCast(SkFloat2Bits(x));
+}
+
+/** Return the floor of the float as an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntFloor(float x) {
+    return SkFloatBits_toIntFloor(SkFloat2Bits(x));
+}
+
+/** Return the float rounded to an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntRound(float x) {
+    return SkFloatBits_toIntRound(SkFloat2Bits(x));
+}
+
+/** Return the ceiling of the float as an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntCeil(float x) {
+    return SkFloatBits_toIntCeil(SkFloat2Bits(x));
+}
+
+#endif
+
+//  Scalar wrappers for float-bit routines
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkScalarAs2sCompliment(x)    SkFloatAs2sCompliment(x)
+    #define Sk2sComplimentAsScalar(x)    Sk2sComplimentAsFloat(x)
+#else
+    #define SkScalarAs2sCompliment(x)    (x)
+    #define Sk2sComplimentAsScalar(x)    (x)
+#endif
+
+#endif
+
diff --git a/legacy/include/core/SkFloatingPoint.h b/legacy/include/core/SkFloatingPoint.h
new file mode 100644
index 0000000..ee91cd9
--- /dev/null
+++ b/legacy/include/core/SkFloatingPoint.h
@@ -0,0 +1,92 @@
+
+/*
+ * 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 SkFloatingPoint_DEFINED
+#define SkFloatingPoint_DEFINED
+
+#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
+static inline float sk_float_pow(float base, float exp) {
+    return static_cast<float>(pow(static_cast<double>(base),
+                                  static_cast<double>(exp)));
+}
+
+static inline float sk_float_copysign(float x, float y) {
+    int32_t xbits = SkFloat2Bits(x);
+    int32_t ybits = SkFloat2Bits(y);
+    return SkBits2Float((xbits & 0x7FFFFFFF) | (ybits & 0x80000000));
+}
+
+#ifdef SK_BUILD_FOR_WINCE
+    #define sk_float_sqrt(x)        (float)::sqrt(x)
+    #define sk_float_sin(x)         (float)::sin(x)
+    #define sk_float_cos(x)         (float)::cos(x)
+    #define sk_float_tan(x)         (float)::tan(x)
+    #define sk_float_acos(x)        (float)::acos(x)
+    #define sk_float_asin(x)        (float)::asin(x)
+    #define sk_float_atan2(y,x)     (float)::atan2(y,x)
+    #define sk_float_abs(x)         (float)::fabs(x)
+    #define sk_float_mod(x,y)       (float)::fmod(x,y)
+    #define sk_float_exp(x)         (float)::exp(x)
+    #define sk_float_log(x)         (float)::log(x)
+    #define sk_float_floor(x)       (float)::floor(x)
+    #define sk_float_ceil(x)        (float)::ceil(x)
+#else
+    #define sk_float_sqrt(x)        sqrtf(x)
+    #define sk_float_sin(x)         sinf(x)
+    #define sk_float_cos(x)         cosf(x)
+    #define sk_float_tan(x)         tanf(x)
+    #define sk_float_floor(x)       floorf(x)
+    #define sk_float_ceil(x)        ceilf(x)
+#ifdef SK_BUILD_FOR_MAC
+    #define sk_float_acos(x)        static_cast<float>(acos(x))
+    #define sk_float_asin(x)        static_cast<float>(asin(x))
+#else
+    #define sk_float_acos(x)        acosf(x)
+    #define sk_float_asin(x)        asinf(x)
+#endif
+    #define sk_float_atan2(y,x)     atan2f(y,x)
+    #define sk_float_abs(x)         fabsf(x)
+    #define sk_float_mod(x,y)       fmodf(x,y)
+    #define sk_float_exp(x)         expf(x)
+    #define sk_float_log(x)         logf(x)
+#endif
+
+#ifdef SK_BUILD_FOR_WIN
+    #define sk_float_isfinite(x)    _finite(x)
+    #define sk_float_isnan(x)       _isnan(x)
+    static inline int sk_float_isinf(float x) {
+        int32_t bits = SkFloat2Bits(x);
+        return (bits << 1) == (0xFF << 24);
+    }
+#else
+    #define sk_float_isfinite(x)    isfinite(x)
+    #define sk_float_isnan(x)       isnan(x)
+    #define sk_float_isinf(x)       isinf(x)
+#endif
+
+#ifdef SK_USE_FLOATBITS
+    #define sk_float_floor2int(x)   SkFloatToIntFloor(x)
+    #define sk_float_round2int(x)   SkFloatToIntRound(x)
+    #define sk_float_ceil2int(x)    SkFloatToIntCeil(x)
+#else
+    #define sk_float_floor2int(x)   (int)sk_float_floor(x)
+    #define sk_float_round2int(x)   (int)sk_float_floor((x) + 0.5f)
+    #define sk_float_ceil2int(x)    (int)sk_float_ceil(x)
+#endif
+
+#endif
+#endif
diff --git a/legacy/include/core/SkFontHost.h b/legacy/include/core/SkFontHost.h
new file mode 100644
index 0000000..ace08d8
--- /dev/null
+++ b/legacy/include/core/SkFontHost.h
@@ -0,0 +1,302 @@
+
+/*
+ * 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 SkFontHost_DEFINED
+#define SkFontHost_DEFINED
+
+#include "SkScalerContext.h"
+#include "SkTypeface.h"
+
+class SkDescriptor;
+class SkStream;
+class SkWStream;
+
+typedef uint32_t SkFontTableTag;
+
+/** \class SkFontHost
+
+    This class is ported to each environment. It is responsible for bridging
+    the gap between the (sort of) abstract class SkTypeface and the
+    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
+    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
+    return seperate typeface instances in that case, or it may choose to use a
+    cache and return the same instance (but calling typeface->ref(), since the
+    caller is always responsible for calling unref() on each instance that is
+    returned). Either way, the fontID for those instance(s) will be the same.
+    In addition, the fontID should never be set to 0. That value is used as a
+    sentinel to indicate no-font-id.
+
+    The major aspects are:
+    1) Given either a name/style, return a subclass of SkTypeface that
+        references the closest matching font available on the host system.
+    2) Given the data for a font (either in a stream or a file name), return
+        a typeface that allows access to that data.
+    3) Each typeface instance carries a 32bit ID for its corresponding font.
+        SkFontHost turns that ID into a stream to access the font's data.
+    4) Given a font ID, return a subclass of SkScalerContext, which connects a
+        font scaler (e.g. freetype or other) to the font's data.
+    5) Utilites to manage the font cache (budgeting) and gamma correction
+*/
+class SK_API SkFontHost {
+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.
+        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
+        represent a valid font, returns null.
+
+        If a typeface instance is returned, the caller is responsible for
+        calling unref() on the typeface when they are finished with it.
+
+        The returned typeface may or may not have called ref() on the stream
+        parameter. If the typeface has not called ref(), then it may have made
+        a copy of the releveant data. In either case, the caller is still
+        responsible for its refcnt ownership of the stream.
+     */
+    static SkTypeface* CreateTypefaceFromStream(SkStream*);
+
+    /** Return a new typeface from the specified file path. If the file does not
+        represent a valid font, this returns null. If a typeface is returned,
+        the caller is responsible for calling unref() when it is no longer used.
+     */
+    static SkTypeface* CreateTypefaceFromFile(const char path[]);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Return a new stream to read the font data, or null if the uniqueID does
+        not match an existing typeface. .The caller must call stream->unref()
+        when it is finished reading the data.
+    */
+    static SkStream* OpenStream(SkFontID uniqueID);
+
+    /** Some fonts are stored in files. If that is true for the fontID, then
+        this returns the byte length of the full file path. If path is not null,
+        then the full path is copied into path (allocated by the caller), up to
+        length bytes. If index is not null, then it is set to the truetype
+        collection index for this font, or 0 if the font is not in a collection.
+
+        Note: GetFileName does not assume that path is a null-terminated string,
+        so when it succeeds, it only copies the bytes of the file name and
+        nothing else (i.e. it copies exactly the number of bytes returned by the
+        function. If the caller wants to treat path[] as a C string, it must be
+        sure that it is allocated at least 1 byte larger than the returned size,
+        and it must copy in the terminating 0.
+
+        If the fontID does not correspond to a file, then the function returns
+        0, and the path and index parameters are ignored.
+
+        @param fontID   The font whose file name is being queried
+        @param path     Either NULL, or storage for receiving up to length bytes
+                        of the font's file name. Allocated by the caller.
+        @param length   The maximum space allocated in path (by the caller).
+                        Ignored if path is NULL.
+        @param index    Either NULL, or receives the TTC index for this font.
+                        If the font is not a TTC, then will be set to 0.
+        @return The byte length of th font's file name, or 0 if the font is not
+                baked by a file.
+     */
+    static size_t GetFileName(SkFontID fontID, char path[], size_t length,
+                              int32_t* index);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Write a unique identifier to the stream, so that the same typeface can
+        be retrieved with Deserialize().
+    */
+    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.
+     */
+    static SkTypeface* Deserialize(SkStream*);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Return a subclass of SkScalarContext
+    */
+    static SkScalerContext* CreateScalerContext(const SkDescriptor* desc);
+
+    /**
+     *  Given a "current" fontID, return the next logical fontID to use
+     *  when searching fonts for a given unicode value. Typically the caller
+     *  will query a given font, and if a unicode value is not supported, they
+     *  will call this, and if 0 is not returned, will search that font, and so
+     *  on. This process must be finite, and when the fonthost sees a
+     *  font with no logical successor, it must return 0.
+     *
+     *  The original fontID is also provided. This is the initial font that was
+     *  stored in the typeface of the caller. It is provided as an aid to choose
+     *  the best next logical font. e.g. If the original font was bold or serif,
+     *  but the 2nd in the logical chain was plain, then a subsequent call to
+     *  get the 3rd can still inspect the original, and try to match its
+     *  stylistic attributes.
+     */
+    static SkFontID NextLogicalFont(SkFontID currFontID, SkFontID origFontID);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    /*
+     * 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);
+#endif
+
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Given a filled-out rec, the fonthost may decide to modify it to reflect
+        what the host is actually capable of fulfilling. For example, if the
+        rec is requesting a level of hinting that, for this host, maps some
+        other level (e.g. kFull -> kNormal), it should update the rec to reflect
+        what will actually be done. This is an optimization so that the font
+        cache does not contain different recs (i.e. keys) that in reality map to
+        the same output.
+
+        A lazy (but valid) fonthost can do nothing in its FilterRec routine.
+     */
+    static void FilterRec(SkScalerContext::Rec* rec);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Retrieve detailed typeface metrics.  Used by the PDF backend.
+        @param perGlyphInfo Indicate what glyph specific information (advances,
+                            names, etc.) should be populated.
+        @return The returned object has already been referenced.  NULL is
+                returned if the font is not found.
+        @param glyphIDs  For per-glyph info, specify subset of the font by
+                         giving glyph ids.  Each integer represents a glyph
+                         id.  Passing NULL means all glyphs in the font.
+        @param glyphIDsCount Number of elements in subsetGlyphIds. Ignored if
+                             glyphIDs is NULL.
+     */
+    static SkAdvancedTypefaceMetrics* GetAdvancedTypefaceMetrics(
+            SkFontID fontID,
+            SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+            const uint32_t* glyphIDs,
+            uint32_t glyphIDsCount);
+
+    /** Return the number of tables in the font
+     */
+    static int CountTables(SkFontID);
+
+    /** 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.
+     */
+    static int GetTableTags(SkFontID, SkFontTableTag[]);
+
+    /** Given a table tag, return the size of its contents, or 0 if not present
+     */
+    static size_t GetTableSize(SkFontID, SkFontTableTag);
+
+    /** 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.
+     */
+    static size_t GetTableData(SkFontID fontID, SkFontTableTag tag,
+                               size_t offset, size_t length, void* data);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** 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.
+    */
+    enum LCDOrientation {
+        kHorizontal_LCDOrientation = 0,    //!< this is the default
+        kVertical_LCDOrientation   = 1,
+    };
+
+    static void SetSubpixelOrientation(LCDOrientation orientation);
+    static LCDOrientation GetSubpixelOrientation();
+
+    /** LCD color elements can vary in order. For subpixel text we need to know
+        the order which the LCDs uses so that the color fringes are in the
+        correct place.
+
+        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.
+
+        kNONE_LCDOrder means that the subpixel elements are not spatially
+        separated in any usable fashion.
+     */
+    enum LCDOrder {
+        kRGB_LCDOrder = 0,    //!< this is the default
+        kBGR_LCDOrder = 1,
+        kNONE_LCDOrder = 2,
+    };
+
+    static void SetSubpixelOrder(LCDOrder order);
+    static LCDOrder GetSubpixelOrder();
+
+#ifdef SK_BUILD_FOR_ANDROID
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Return the number of font units per em.
+     *
+     * @param fontID the font to query.
+     * @return the number of font units per em or 0 on error.
+     */
+    static uint32_t GetUnitsPerEm(SkFontID fontID);
+#endif
+
+    /** If Skia is running in a constrained environment and the typeface
+        implementation is handle based, the typeface data may become
+        unavailable asynchronously. If a font host or scaler context method is
+        unable to access font data, it may call this function as a request to
+        make the handle contained in the typeface useable.
+    */
+    static void EnsureTypefaceAccessible(const SkTypeface& typeface);
+};
+
+#endif
diff --git a/legacy/include/core/SkGeometry.h b/legacy/include/core/SkGeometry.h
new file mode 100644
index 0000000..26f27ba
--- /dev/null
+++ b/legacy/include/core/SkGeometry.h
@@ -0,0 +1,209 @@
+
+/*
+ * 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 SkGeometry_DEFINED
+#define SkGeometry_DEFINED
+
+#include "SkMatrix.h"
+
+/** An XRay is a half-line that runs from the specific point/origin to
+    +infinity in the X direction. e.g. XRay(3,5) is the half-line
+    (3,5)....(infinity, 5)
+ */
+typedef SkPoint SkXRay;
+
+/** Given a line segment from pts[0] to pts[1], and an xray, return true if
+    they intersect. Optional outgoing "ambiguous" argument indicates
+    whether the answer is ambiguous because the query occurred exactly at
+    one of the endpoints' y coordinates, indicating that another query y
+    coordinate is preferred for robustness.
+*/
+bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2],
+                       bool* ambiguous = NULL);
+
+/** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the
+    equation.
+*/
+int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]);
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Set pt to the point on the src quadratic specified by t. t must be
+    0 <= t <= 1.0
+*/
+void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt,
+                  SkVector* tangent = NULL);
+void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt,
+                      SkVector* tangent = NULL);
+
+/** Given a src quadratic bezier, chop it at the specified t value,
+    where 0 < t < 1, and return the two new quadratics in dst:
+    dst[0..2] and dst[2..4]
+*/
+void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t);
+
+/** Given a src quadratic bezier, chop it at the specified t == 1/2,
+    The new quads are returned in dst[0..2] and dst[2..4]
+*/
+void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]);
+
+/** Given the 3 coefficients for a quadratic bezier (either X or Y values), look
+    for extrema, and return the number of t-values that are found that represent
+    these extrema. If the quadratic has no extrema betwee (0..1) exclusive, the
+    function returns 0.
+    Returned count      tValues[]
+    0                   ignored
+    1                   0 < tValues[0] < 1
+*/
+int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValues[1]);
+
+/** Given 3 points on a quadratic bezier, chop it into 1, 2 beziers such that
+    the resulting beziers are monotonic in Y. This is called by the scan converter.
+    Depending on what is returned, dst[] is treated as follows
+    0   dst[0..2] is the original quad
+    1   dst[0..2] and dst[2..4] are the two new quads
+*/
+int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]);
+int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5]);
+
+/** Given 3 points on a quadratic bezier, divide it into 2 quadratics
+    if the point of maximum curvature exists on the quad segment.
+    Depending on what is returned, dst[] is treated as follows
+    1   dst[0..2] is the original quad
+    2   dst[0..2] and dst[2..4] are the two new quads
+    If dst == null, it is ignored and only the count is returned.
+*/
+int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]);
+
+/** Given 3 points on a quadratic bezier, use degree elevation to
+    convert it into the cubic fitting the same curve. The new cubic
+    curve is returned in dst[0..3].
+*/
+SK_API void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]);
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Convert from parametric from (pts) to polynomial coefficients
+    coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3]
+*/
+void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]);
+
+/** Set pt to the point on the src cubic specified by t. t must be
+    0 <= t <= 1.0
+*/
+void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* locOrNull,
+                   SkVector* tangentOrNull, SkVector* curvatureOrNull);
+
+/** Given a src cubic bezier, chop it at the specified t value,
+    where 0 < t < 1, and return the two new cubics in dst:
+    dst[0..3] and dst[3..6]
+*/
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t);
+/** Given a src cubic bezier, chop it at the specified t values,
+    where 0 < t < 1, and return the new cubics in dst:
+    dst[0..3],dst[3..6],...,dst[3*t_count..3*(t_count+1)]
+*/
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar t[],
+                   int t_count);
+
+/** Given a src cubic bezier, chop it at the specified t == 1/2,
+    The new cubics are returned in dst[0..3] and dst[3..6]
+*/
+void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]);
+
+/** Given the 4 coefficients for a cubic bezier (either X or Y values), look
+    for extrema, and return the number of t-values that are found that represent
+    these extrema. If the cubic has no extrema betwee (0..1) exclusive, the
+    function returns 0.
+    Returned count      tValues[]
+    0                   ignored
+    1                   0 < tValues[0] < 1
+    2                   0 < tValues[0] < tValues[1] < 1
+*/
+int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
+                       SkScalar tValues[2]);
+
+/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
+    the resulting beziers are monotonic in Y. This is called by the scan converter.
+    Depending on what is returned, dst[] is treated as follows
+    0   dst[0..3] is the original cubic
+    1   dst[0..3] and dst[3..6] are the two new cubics
+    2   dst[0..3], dst[3..6], dst[6..9] are the three new cubics
+    If dst == null, it is ignored and only the count is returned.
+*/
+int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]);
+int SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10]);
+
+/** Given a cubic bezier, return 0, 1, or 2 t-values that represent the
+    inflection points.
+*/
+int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2]);
+
+/** Return 1 for no chop, 2 for having chopped the cubic at a single
+    inflection point, 3 for having chopped at 2 inflection points.
+    dst will hold the resulting 1, 2, or 3 cubics.
+*/
+int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10]);
+
+int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]);
+int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13],
+                              SkScalar tValues[3] = NULL);
+
+/** Given a monotonic cubic bezier, determine whether an xray intersects the
+    cubic.
+    By definition the cubic is open at the starting point; in other
+    words, if pt.fY is equivalent to cubic[0].fY, and pt.fX is to the
+    left of the curve, the line is not considered to cross the curve,
+    but if it is equal to cubic[3].fY then it is considered to
+    cross.
+    Optional outgoing "ambiguous" argument indicates whether the answer is
+    ambiguous because the query occurred exactly at one of the endpoints' y
+    coordinates, indicating that another query y coordinate is preferred
+    for robustness.
+ */
+bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4],
+                                 bool* ambiguous = NULL);
+
+/** Given an arbitrary cubic bezier, return the number of times an xray crosses
+    the cubic. Valid return values are [0..3]
+    By definition the cubic is open at the starting point; in other
+    words, if pt.fY is equivalent to cubic[0].fY, and pt.fX is to the
+    left of the curve, the line is not considered to cross the curve,
+    but if it is equal to cubic[3].fY then it is considered to
+    cross.
+    Optional outgoing "ambiguous" argument indicates whether the answer is
+    ambiguous because the query occurred exactly at one of the endpoints' y
+    coordinates or at a tangent point, indicating that another query y
+    coordinate is preferred for robustness.
+ */
+int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4],
+                               bool* ambiguous = NULL);
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum SkRotationDirection {
+    kCW_SkRotationDirection,
+    kCCW_SkRotationDirection
+};
+
+/** Maximum number of points needed in the quadPoints[] parameter for
+    SkBuildQuadArc()
+*/
+#define kSkBuildQuadArcStorage  17
+
+/** Given 2 unit vectors and a rotation direction, fill out the specified
+    array of points with quadratic segments. Return is the number of points
+    written to, which will be { 0, 3, 5, 7, ... kSkBuildQuadArcStorage }
+
+    matrix, if not null, is appled to the points before they are returned.
+*/
+int SkBuildQuadArc(const SkVector& unitStart, const SkVector& unitStop,
+                   SkRotationDirection, const SkMatrix*, SkPoint quadPoints[]);
+
+#endif
diff --git a/legacy/include/core/SkGraphics.h b/legacy/include/core/SkGraphics.h
new file mode 100644
index 0000000..c89758b
--- /dev/null
+++ b/legacy/include/core/SkGraphics.h
@@ -0,0 +1,87 @@
+
+/*
+ * 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 SkGraphics_DEFINED
+#define SkGraphics_DEFINED
+
+#include "SkTypes.h"
+
+class SkGraphics {
+public:
+    /**
+     *  Call this at process initialization time if your environment does not
+     *  permit static global initializers that execute code. Note that 
+     *  Init() is not thread-safe.
+     */
+    static void Init();
+
+    /**
+     *  Call this to release any memory held privately, such as the font cache.
+     */
+    static void Term();
+
+    /**
+     *  Return the version numbers for the library. If the parameter is not
+     *  null, it is set to the version number.
+     */
+    static void GetVersion(int32_t* major, int32_t* minor, int32_t* patch);
+
+    /**
+     *  Return 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.
+     *  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.
+     *
+     *  This function returns the previous setting, as if GetFontCacheLimit()
+     *  had be called before the new limit was set.
+     */
+    static size_t SetFontCacheLimit(size_t bytes);
+
+    /**
+     *  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:
+     *  font-cache-limit=12345678
+     *
+     *  The flags format is name=value[;name=value...] with no spaces.
+     *  This format is subject to change.
+     */
+    static void SetFlags(const char* flags);
+    
+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
+        with the C++ runtime to call SkGraphics::FreeCaches()
+    */
+    static void InstallNewHandler();
+};
+
+class SkAutoGraphics {
+public:
+    SkAutoGraphics() {
+        SkGraphics::Init();
+    }
+    ~SkAutoGraphics() {
+        SkGraphics::Term();
+    }
+};
+
+#endif
+
diff --git a/legacy/include/core/SkImageFilter.h b/legacy/include/core/SkImageFilter.h
new file mode 100644
index 0000000..7d7c140
--- /dev/null
+++ b/legacy/include/core/SkImageFilter.h
@@ -0,0 +1,113 @@
+/*
+ * 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 SkImageFilter_DEFINED
+#define SkImageFilter_DEFINED
+
+#include "SkFlattenable.h"
+
+class SkBitmap;
+class SkDevice;
+class SkMatrix;
+struct SkPoint;
+
+/**
+ *  Experimental.
+ *
+ *  Base class for image filters. If one is installed in the paint, then
+ *  all drawing occurs as usual, but it is as if the drawing happened into an
+ *  offscreen (before the xfermode is applied). This offscreen bitmap will
+ *  then be handed to the imagefilter, who in turn creates a new bitmap which
+ *  is what will finally be drawn to the device (using the original xfermode).
+ *
+ *  THIS SIGNATURE IS TEMPORARY
+ *
+ *  There are several weaknesses in this function signature:
+ *  1. Does not expose the destination/target device, so filters that can draw
+ *     directly to it are unable to take advantage of that optimization.
+ *  2. Does not expose a way to create a "compabitible" image (i.e. gpu -> gpu)
+ *  3. As with #1, the filter is unable to "read" the dest (which would be slow)
+ *
+ *  Therefore, we should not create any real dependencies on this API yet -- it
+ *  is being checked in as a check-point so we can explore these and other
+ *  considerations.
+ */
+class SK_API SkImageFilter : public SkFlattenable {
+public:
+    class Proxy {
+    public:
+        virtual SkDevice* createDevice(int width, int height) = 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() {};
+    };
+
+    /**
+     *  Request a new (result) image to be created from the src image.
+     *  If the src has no pixels (isNull()) then the request just wants to
+     *  receive the config and width/height of the result.
+     *
+     *  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. 
+     *
+     *  If the result image cannot be created, return false, in which case both
+     *  the result and offset parameters will be ignored by the caller.
+     */
+    bool filterImage(Proxy*, const SkBitmap& src, const SkMatrix& ctm,
+                     SkBitmap* result, SkIPoint* offset);
+
+    /**
+     *  Given the src bounds of an image, this returns the bounds of the result
+     *  image after the filter has been applied.
+     */
+    bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst);
+
+    /**
+     *  Experimental.
+     *
+     *  If the filter can be expressed as a gaussian-blur, return true and
+     *  set the sigma to the values for horizontal and vertical.
+     */
+    virtual bool asABlur(SkSize* sigma) const;
+
+    /**
+     *  Experimental.
+     *
+     *  If the filter can be expressed as an erode, return true and
+     *  set the radius in X and Y.
+     */
+    virtual bool asAnErode(SkISize* radius) const;
+
+    /**
+     *  Experimental.
+     *
+     *  If the filter can be expressed as a dilation, return true and
+     *  set the radius in X and Y.
+     */
+    virtual bool asADilate(SkISize* radius) const;
+
+protected:
+    SkImageFilter() {}
+    explicit SkImageFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
+
+    // Default impl returns false
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset);
+    // Default impl copies src into dst and returns true
+    virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*);
+
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/legacy/include/core/SkLanguage.h b/legacy/include/core/SkLanguage.h
new file mode 100644
index 0000000..923008e
--- /dev/null
+++ b/legacy/include/core/SkLanguage.h
@@ -0,0 +1,70 @@
+
+/*
+ * 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 SkLanguage_DEFINED
+#define SkLanguage_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+
+#include "SkString.h"
+
+struct SkLanguageInfo {
+    SkLanguageInfo(const char* tag) : fTag(tag) { }
+    SkString fTag; //! BCP 47 language identifier
+};
+
+/** \class SkLanguage
+
+    The SkLanguage class represents a human written language, and is used by
+    text draw operations to determine which glyph to draw when drawing
+    characters with variants (ie Han-derived characters).
+*/
+class SkLanguage {
+public:
+    SkLanguage() : fInfo(getInfo("")) { }
+    SkLanguage(const char* tag) : fInfo(getInfo(tag)) { }
+    SkLanguage(const SkLanguage& b) : fInfo(b.fInfo) { }
+
+    /** Gets a BCP 47 language identifier for this SkLanguage.
+        @return a BCP 47 language identifier representing this language
+    */
+    const SkString& getTag() const { return fInfo->fTag; }
+
+    /** Performs BCP 47 fallback to return an SkLanguage one step more general.
+        @return an SkLanguage one step more general
+    */
+    SkLanguage getParent() const;
+
+    bool operator==(const SkLanguage& b) const {
+        return fInfo == b.fInfo;
+    }
+    bool operator!=(const SkLanguage& b) const {
+        return fInfo != b.fInfo;
+    }
+    bool operator<(const SkLanguage& b) const {
+        return fInfo < b.fInfo;
+    }
+    bool operator>(const SkLanguage& b) const {
+        return fInfo > b.fInfo;
+    }
+    SkLanguage& operator=(const SkLanguage& b) {
+        fInfo = b.fInfo;
+        return *this;
+    }
+
+private:
+    const SkLanguageInfo* fInfo;
+
+    static const SkLanguageInfo* getInfo(const char* tag);
+};
+
+#endif // #ifdef SK_BUILD_FOR_ANDROID
+#endif // #ifndef SkLanguage_DEFINED
diff --git a/legacy/include/core/SkLineClipper.h b/legacy/include/core/SkLineClipper.h
new file mode 100644
index 0000000..1958395
--- /dev/null
+++ b/legacy/include/core/SkLineClipper.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 SkLineClipper_DEFINED
+#define SkLineClipper_DEFINED
+
+#include "SkRect.h"
+#include "SkPoint.h"
+
+class SkLineClipper {
+public:
+    enum {
+        kMaxPoints = 4
+    };
+
+    /*  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]
+            2nd segment: lines[1]..lines[2]
+            3rd segment: lines[2]..lines[3]
+     */
+    static int ClipLine(const SkPoint pts[2], const SkRect& clip,
+                        SkPoint lines[kMaxPoints]);
+
+    /*  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.
+     */
+    static bool IntersectLine(const SkPoint src[2], const SkRect& clip,
+                              SkPoint dst[2]);
+};
+
+#endif
+
diff --git a/legacy/include/core/SkMMapStream.h b/legacy/include/core/SkMMapStream.h
new file mode 100644
index 0000000..19ba634
--- /dev/null
+++ b/legacy/include/core/SkMMapStream.h
@@ -0,0 +1,30 @@
+
+/*
+ * 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 SkMMapStream_DEFINED
+#define SkMMapStream_DEFINED
+
+#include "SkStream.h"
+
+class SkMMAPStream : public SkMemoryStream {
+public:
+    SkMMAPStream(const char filename[]);
+    virtual ~SkMMAPStream();
+
+    virtual void setMemory(const void* data, size_t length, bool);
+private:
+    void*   fAddr;
+    size_t  fSize;
+    
+    void closeMMap();
+    
+    typedef SkMemoryStream INHERITED;
+};
+
+#endif
diff --git a/legacy/include/core/SkMallocPixelRef.h b/legacy/include/core/SkMallocPixelRef.h
new file mode 100644
index 0000000..2ceb826
--- /dev/null
+++ b/legacy/include/core/SkMallocPixelRef.h
@@ -0,0 +1,59 @@
+
+/*
+ * 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 SkMallocPixelRef_DEFINED
+#define SkMallocPixelRef_DEFINED
+
+#include "SkPixelRef.h"
+
+/** We explicitly use the same allocator for our pixels that SkMask does,
+    so that we can freely assign memory allocated by one class to the other.
+*/
+class SkMallocPixelRef : public SkPixelRef {
+public:
+    /** Allocate the specified buffer for pixels. The memory is freed when the
+        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);
+    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_PIXEL_REF_REGISTRAR()
+protected:
+    // overrides from SkPixelRef
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+
+    SkMallocPixelRef(SkFlattenableReadBuffer& buffer);
+
+    void*           fStorage;
+
+private:
+
+    size_t          fSize;
+    SkColorTable*   fCTable;
+
+    typedef SkPixelRef INHERITED;
+};
+
+
+#endif
diff --git a/legacy/include/core/SkMask.h b/legacy/include/core/SkMask.h
new file mode 100644
index 0000000..3f9a114
--- /dev/null
+++ b/legacy/include/core/SkMask.h
@@ -0,0 +1,149 @@
+
+/*
+ * 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 SkMask_DEFINED
+#define SkMask_DEFINED
+
+#include "SkRect.h"
+
+/** \class SkMask
+    SkMask is used to describe alpha bitmaps, either 1bit, 8bit, or
+    the 3-channel 3D format. These are passed to SkMaskFilter objects.
+*/
+struct SkMask {
+    enum Format {
+        kBW_Format, //!< 1bit per pixel mask (e.g. monochrome)
+        kA8_Format, //!< 8bits per pixel mask (e.g. antialiasing)
+        k3D_Format, //!< 3 8bit per pixl planes: alpha, mul, add
+        kARGB32_Format,         //!< SkPMColor
+        kLCD16_Format,          //!< 565 alpha for r/g/b
+        kLCD32_Format           //!< 888 alpha for r/g/b
+    };
+
+    enum {
+        kCountMaskFormats = kLCD32_Format + 1
+    };
+
+    uint8_t*    fImage;
+    SkIRect     fBounds;
+    uint32_t    fRowBytes;
+    Format      fFormat;
+
+    /** Returns true if the mask is empty: i.e. it has an empty bounds.
+     */
+    bool isEmpty() const { return fBounds.isEmpty(); }
+
+    /** Return the byte size of the mask, assuming only 1 plane.
+        Does not account for k3D_Format. For that, use computeTotalImageSize().
+        If there is an overflow of 32bits, then returns 0.
+    */
+    size_t computeImageSize() const;
+
+    /** Return the byte size of the mask, taking into account
+        any extra planes (e.g. k3D_Format).
+        If there is an overflow of 32bits, then returns 0.
+    */
+    size_t computeTotalImageSize() const;
+
+    /** Returns the address of the byte that holds the specified bit.
+        Asserts that the mask is kBW_Format, and that x,y are in range.
+        x,y are in the same coordiate space as fBounds.
+    */
+    uint8_t* getAddr1(int x, int y) const {
+        SkASSERT(kBW_Format == fFormat);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        return fImage + ((x - fBounds.fLeft) >> 3) + (y - fBounds.fTop) * fRowBytes;
+    }
+
+    /** Returns the address of the specified byte.
+        Asserts that the mask is kA8_Format, and that x,y are in range.
+        x,y are in the same coordiate space as fBounds.
+    */
+    uint8_t* getAddr8(int x, int y) const {
+        SkASSERT(kA8_Format == fFormat);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes;
+    }
+
+    /**
+     *  Return the address of the specified 16bit mask. In the debug build,
+     *  this asserts that the mask's format is kLCD16_Format, and that (x,y)
+     *  are contained in the mask's fBounds.
+     */
+    uint16_t* getAddrLCD16(int x, int y) const {
+        SkASSERT(kLCD16_Format == fFormat);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        uint16_t* row = (uint16_t*)(fImage + (y - fBounds.fTop) * fRowBytes);
+        return row + (x - fBounds.fLeft);
+    }
+
+    /**
+     *  Return the address of the specified 32bit mask. In the debug build,
+     *  this asserts that the mask's format is kLCD32_Format, and that (x,y)
+     *  are contained in the mask's fBounds.
+     */
+    uint32_t* getAddrLCD32(int x, int y) const {
+        SkASSERT(kLCD32_Format == fFormat);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        uint32_t* row = (uint32_t*)(fImage + (y - fBounds.fTop) * fRowBytes);
+        return row + (x - fBounds.fLeft);
+    }
+
+    /**
+     *  Returns the address of the specified pixel, computing the pixel-size
+     *  at runtime based on the mask format. This will be slightly slower than
+     *  using one of the routines where the format is implied by the name
+     *  e.g. getAddr8 or getAddrLCD32.
+     *
+     *  x,y must be contained by the mask's bounds (this is asserted in the
+     *  debug build, but not checked in the release build.)
+     *
+     *  This should not be called with kBW_Format, as it will give unspecified
+     *  results (and assert in the debug build).
+     */
+    void* getAddr(int x, int y) const;
+
+    static uint8_t* AllocImage(size_t bytes);
+    static void FreeImage(void* image);
+
+    enum CreateMode {
+        kJustComputeBounds_CreateMode,      //!< compute bounds and return
+        kJustRenderImage_CreateMode,        //!< render into preallocate mask
+        kComputeBoundsAndRenderImage_CreateMode  //!< compute bounds, alloc image and render into it
+    };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  \class SkAutoMaskImage
+ *
+ *  Stack class used to manage the fImage buffer in a SkMask.
+ *  When this object loses scope, the buffer is freed with SkMask::FreeImage().
+ */
+class SkAutoMaskFreeImage {
+public:
+    SkAutoMaskFreeImage(uint8_t* maskImage) {
+        fImage = maskImage;
+    }
+    
+    ~SkAutoMaskFreeImage() {
+        SkMask::FreeImage(fImage);
+    }
+
+private:
+    uint8_t* fImage;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkMaskFilter.h b/legacy/include/core/SkMaskFilter.h
new file mode 100644
index 0000000..2808de7
--- /dev/null
+++ b/legacy/include/core/SkMaskFilter.h
@@ -0,0 +1,112 @@
+
+/*
+ * 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 SkMaskFilter_DEFINED
+#define SkMaskFilter_DEFINED
+
+#include "SkFlattenable.h"
+#include "SkMask.h"
+
+class SkBlitter;
+class SkBounder;
+class SkMatrix;
+class SkPath;
+class SkRasterClip;
+
+/** \class SkMaskFilter
+
+    SkMaskFilter is the base class for object that perform transformations on
+    an alpha-channel mask before drawing it. A subclass of SkMaskFilter may be
+    installed into a SkPaint. Once there, each time a primitive is drawn, it
+    is first scan converted into a SkMask::kA8_Format mask, and handed to the
+    filter, calling its filterMask() method. If this returns true, then the
+    new mask is used to render into the device.
+
+    Blur and emboss are implemented as subclasses of SkMaskFilter.
+*/
+class SkMaskFilter : public SkFlattenable {
+public:
+    SkMaskFilter() {}
+
+    /** Returns the format of the resulting mask that this subclass will return
+        when its filterMask() method is called.
+    */
+    virtual SkMask::Format getFormat() = 0;
+
+    /** Create a new mask by filter the src mask.
+        If src.fImage == null, then do not allocate or create the dst image
+        but do fill out the other fields in dstMask.
+        If you do allocate a dst image, use SkMask::AllocImage()
+        If this returns false, dst mask is ignored.
+        @param  dst the result of the filter. If src.fImage == null, dst should not allocate its image
+        @param src the original image to be filtered.
+        @param matrix the CTM
+        @param margin   if not null, return the buffer dx/dy need when calculating the effect. Used when
+                        drawing a clipped object to know how much larger to allocate the src before
+                        applying the filter. If returning false, ignore this parameter.
+        @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& ) {}
+
+    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
+    };
+
+    struct BlurInfo {
+        SkScalar fRadius;
+        bool     fIgnoreTransform;
+        bool     fHighQuality;
+    };
+
+    /**
+     *  Optional method for maskfilters that can be described as a blur. If so,
+     *  they return the corresponding BlurType and set the fields in BlurInfo
+     *  (if not null). If they cannot be described as a blur, they return
+     *  kNone_BlurType and ignore the info parameter.
+     */
+    virtual BlurType asABlur(BlurInfo*) const;
+
+    /**
+     * The fast bounds function is used to enable the paint to be culled early
+     * in the drawing pipeline. This function accepts the current bounds of the
+     * paint as its src param and the filter adjust those bounds using its
+     * current mask and returns the result using the dest param. Callers are
+     * allowed to provide the same struct for both src and dest so each
+     * implementation must accomodate that behavior.
+     *
+     *  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);
+
+protected:
+    // empty for now, but lets get our subclass to remember to init us for the future
+    SkMaskFilter(SkFlattenableReadBuffer&) {}
+
+private:
+    friend class SkDraw;
+
+    /** Helper method that, given a path in device space, will rasterize it into a kA8_Format mask
+     and then call filterMask(). If this returns true, the specified blitter will be called
+     to render that mask. Returns false if filterMask() returned false.
+     This method is not exported to java.
+     */
+    bool filterPath(const SkPath& devPath, const SkMatrix& devMatrix,
+                    const SkRasterClip&, SkBounder*, SkBlitter* blitter);
+};
+
+#endif
+
diff --git a/legacy/include/core/SkMath.h b/legacy/include/core/SkMath.h
new file mode 100644
index 0000000..5889103
--- /dev/null
+++ b/legacy/include/core/SkMath.h
@@ -0,0 +1,231 @@
+
+/*
+ * 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 SkMath_DEFINED
+#define SkMath_DEFINED
+
+#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.
+*/
+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.
+*/
+int32_t SkDivBits(int32_t numer, int32_t denom, int shift);
+
+/** 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
+ */
+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
+*/
+static inline int SkClampMax(int value, int max) {
+    // ensure that max is positive
+    SkASSERT(max >= 0);
+    if (value < 0) {
+        value = 0;
+    }
+    if (value > max) {
+        value = max;
+    }
+    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.
+*/
+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
+*/
+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.
+ */
+static inline bool SkIsPow2(int value) {
+    return (value & (value - 1)) == 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** 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__)
+    static inline int32_t SkMulS16(S16CPU x, S16CPU y) {
+        SkASSERT((int16_t)x == x);
+        SkASSERT((int16_t)y == y);
+        int32_t product;
+        asm("smulbb %0, %1, %2 \n"
+            : "=r"(product)
+            : "r"(x), "r"(y)
+            );
+        return product;
+    }
+#else
+    #ifdef SK_DEBUG
+        static inline int32_t SkMulS16(S16CPU x, S16CPU y) {
+            SkASSERT((int16_t)x == x);
+            SkASSERT((int16_t)y == y);
+            return x * y;
+        }
+    #else
+        #define SkMulS16(x, y)  ((x) * (y))
+    #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
+ */
+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);
+    SkASSERT(shift > 0 && shift <= 8);
+    unsigned prod = SkMulS16(a, b) + (1 << (shift - 1));
+    return (prod + (prod >> shift)) >> shift;
+}
+
+/** 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/legacy/include/core/SkMatrix.h b/legacy/include/core/SkMatrix.h
new file mode 100644
index 0000000..8fdb818
--- /dev/null
+++ b/legacy/include/core/SkMatrix.h
@@ -0,0 +1,641 @@
+
+/*
+ * 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 SkMatrix_DEFINED
+#define SkMatrix_DEFINED
+
+#include "SkRect.h"
+
+class SkString;
+
+#ifdef SK_SCALAR_IS_FLOAT
+    typedef SkScalar SkPersp;
+    #define SkScalarToPersp(x) (x)
+    #define SkPerspToScalar(x) (x)
+#else
+    typedef SkFract SkPersp;
+    #define SkScalarToPersp(x) SkFixedToFract(x)
+    #define SkPerspToScalar(x) SkFractToFixed(x)
+#endif
+
+/** \class SkMatrix
+
+    The SkMatrix class holds a 3x3 matrix for transforming coordinates.
+    SkMatrix does not have a constructor, so it must be explicitly initialized
+    using either reset() - to construct an identity matrix, or one of the set
+    functions (e.g. setTranslate, setRotate, etc.).
+*/
+class SK_API SkMatrix {
+public:
+    /** Enum of bit fields for the mask return by getType().
+        Use this to identify the complexity of the matrix.
+    */
+    enum TypeMask {
+        kIdentity_Mask      = 0,
+        kTranslate_Mask     = 0x01,  //!< set if the matrix has translation
+        kScale_Mask         = 0x02,  //!< set if the matrix has X or Y scale
+        kAffine_Mask        = 0x04,  //!< set if the matrix skews or rotates
+        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.
+    */
+    TypeMask getType() const {
+        if (fTypeMask & kUnknown_Mask) {
+            fTypeMask = this->computeTypeMask();
+        }
+        // only return the public masks
+        return (TypeMask)(fTypeMask & 0xF);
+    }
+
+    /** Returns true if the matrix is identity.
+    */
+    bool isIdentity() const {
+        return this->getType() == 0;
+    }
+
+    /** Returns true if will map a rectangle to another rectangle. This can be
+        true if the matrix is identity, scale-only, or rotates a multiple of
+        90 degrees.
+    */
+    bool rectStaysRect() const {
+        if (fTypeMask & kUnknown_Mask) {
+            fTypeMask = this->computeTypeMask();
+        }
+        return (fTypeMask & kRectStaysRect_Mask) != 0;
+    }
+    // alias for rectStaysRect()
+    bool preservesAxisAlignment() const { return this->rectStaysRect(); }
+
+    /**
+     *  Returns true if the matrix contains perspective elements.
+     */
+    bool hasPerspective() const {
+        return SkToBool(this->getPerspectiveTypeMaskOnly() &
+                        kPerspective_Mask);
+    }
+
+    enum {
+        kMScaleX,
+        kMSkewX,
+        kMTransX,
+        kMSkewY,
+        kMScaleY,
+        kMTransY,
+        kMPersp0,
+        kMPersp1,
+        kMPersp2
+    };
+    
+    /** Affine arrays are in column major order
+        because that's how PDF and XPS like it.
+     */
+    enum {
+        kAScaleX,
+        kASkewY,
+        kASkewX,
+        kAScaleY,
+        kATransX,
+        kATransY
+    };
+
+    SkScalar operator[](int index) const {
+        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]; }
+    SkScalar getSkewX() const { return fMat[kMSkewX]; }
+    SkScalar getTranslateX() const { return fMat[kMTransX]; }
+    SkScalar getTranslateY() const { return fMat[kMTransY]; }
+    SkPersp getPerspX() const { return fMat[kMPersp0]; }
+    SkPersp getPerspY() const { return fMat[kMPersp1]; }
+
+    SkScalar& operator[](int index) {
+        SkASSERT((unsigned)index < 9);
+        this->setTypeMask(kUnknown_Mask);
+        return fMat[index];
+    }
+
+    void set(int index, SkScalar value) {
+        SkASSERT((unsigned)index < 9);
+        fMat[index] = value;
+        this->setTypeMask(kUnknown_Mask);
+    }
+
+    void setScaleX(SkScalar v) { this->set(kMScaleX, v); }
+    void setScaleY(SkScalar v) { this->set(kMScaleY, v); }
+    void setSkewY(SkScalar v) { this->set(kMSkewY, v); }
+    void setSkewX(SkScalar v) { this->set(kMSkewX, v); }
+    void setTranslateX(SkScalar v) { this->set(kMTransX, v); }
+    void setTranslateY(SkScalar v) { this->set(kMTransY, v); }
+    void setPerspX(SkPersp v) { this->set(kMPersp0, v); }
+    void setPerspY(SkPersp v) { this->set(kMPersp1, v); }
+
+    void setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
+                SkScalar skewY, SkScalar scaleY, SkScalar transY,
+                SkPersp persp0, SkPersp persp1, SkPersp persp2) {
+        fMat[kMScaleX] = scaleX;
+        fMat[kMSkewX]  = skewX;
+        fMat[kMTransX] = transX;
+        fMat[kMSkewY]  = skewY;
+        fMat[kMScaleY] = scaleY;
+        fMat[kMTransY] = transY;
+        fMat[kMPersp0] = persp0;
+        fMat[kMPersp1] = persp1;
+        fMat[kMPersp2] = persp2;
+        this->setTypeMask(kUnknown_Mask);
+    }
+        
+    /** Set the matrix to identity
+    */
+    void reset();
+    // alias for reset()
+    void setIdentity() { this->reset(); }
+
+    /** Set the matrix to translate by (dx, dy).
+    */
+    void setTranslate(SkScalar dx, SkScalar dy);
+    /** 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.
+    */
+    void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
+    /** Set the matrix to scale by sx and sy.
+    */
+    void setScale(SkScalar sx, SkScalar sy);
+    /** Set the matrix to scale by 1/divx and 1/divy. Returns false and doesn't
+        touch the matrix if either divx or divy is zero.
+    */
+    bool setIDiv(int divx, int divy);
+    /** Set the matrix to rotate by the specified number of degrees, with a
+        pivot point at (px, py). The pivot point is the coordinate that should
+        remain unchanged by the specified transformation.
+    */
+    void setRotate(SkScalar degrees, SkScalar px, SkScalar py);
+    /** Set the matrix to rotate about (0,0) by the specified number of degrees.
+    */
+    void setRotate(SkScalar degrees);
+    /** Set the matrix to rotate by the specified sine and cosine values, with
+        a pivot point at (px, py). The pivot point is the coordinate that
+        should remain unchanged by the specified transformation.
+    */
+    void setSinCos(SkScalar sinValue, SkScalar cosValue,
+                   SkScalar px, SkScalar py);
+    /** Set the matrix to rotate by the specified sine and cosine values.
+    */
+    void setSinCos(SkScalar sinValue, SkScalar cosValue);
+    /** Set the matrix to skew 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.
+    */
+    void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
+    /** Set the matrix to skew by sx and sy.
+    */
+    void setSkew(SkScalar kx, SkScalar ky);
+    /** Set the matrix to the concatenation of the two specified matrices,
+        returning true if the the result can be represented. Either of the
+        two matrices may also be the target matrix. *this = a * b;
+    */
+    bool setConcat(const SkMatrix& a, const SkMatrix& b);
+
+    /** Preconcats the matrix with the specified translation.
+        M' = M * T(dx, dy)
+    */
+    bool preTranslate(SkScalar dx, SkScalar dy);
+    /** Preconcats the matrix with the specified scale.
+        M' = M * S(sx, sy, px, py)
+    */
+    bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
+    /** Preconcats the matrix with the specified scale.
+        M' = M * S(sx, sy)
+    */
+    bool preScale(SkScalar sx, SkScalar sy);
+    /** Preconcats the matrix with the specified rotation.
+        M' = M * R(degrees, px, py)
+    */
+    bool preRotate(SkScalar degrees, SkScalar px, SkScalar py);
+    /** Preconcats the matrix with the specified rotation.
+        M' = M * R(degrees)
+    */
+    bool preRotate(SkScalar degrees);
+    /** Preconcats the matrix with the specified skew.
+        M' = M * K(kx, ky, px, py)
+    */
+    bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
+    /** Preconcats the matrix with the specified skew.
+        M' = M * K(kx, ky)
+    */
+    bool preSkew(SkScalar kx, SkScalar ky);
+    /** Preconcats the matrix with the specified matrix.
+        M' = M * other
+    */
+    bool preConcat(const SkMatrix& other);
+
+    /** Postconcats the matrix with the specified translation.
+        M' = T(dx, dy) * M
+    */
+    bool postTranslate(SkScalar dx, SkScalar dy);
+    /** Postconcats the matrix with the specified scale.
+        M' = S(sx, sy, px, py) * M
+    */
+    bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
+    /** Postconcats the matrix with the specified scale.
+        M' = S(sx, sy) * M
+    */
+    bool postScale(SkScalar sx, SkScalar sy);
+    /** Postconcats the matrix by dividing it by the specified integers.
+        M' = S(1/divx, 1/divy, 0, 0) * M
+    */
+    bool postIDiv(int divx, int divy);
+    /** Postconcats the matrix with the specified rotation.
+        M' = R(degrees, px, py) * M
+    */
+    bool postRotate(SkScalar degrees, SkScalar px, SkScalar py);
+    /** Postconcats the matrix with the specified rotation.
+        M' = R(degrees) * M
+    */
+    bool postRotate(SkScalar degrees);
+    /** Postconcats the matrix with the specified skew.
+        M' = K(kx, ky, px, py) * M
+    */
+    bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
+    /** Postconcats the matrix with the specified skew.
+        M' = K(kx, ky) * M
+    */
+    bool postSkew(SkScalar kx, SkScalar ky);
+    /** Postconcats the matrix with the specified matrix.
+        M' = other * M
+    */
+    bool postConcat(const SkMatrix& other);
+
+    enum ScaleToFit {
+        /**
+         * Scale in X and Y independently, so that src matches dst exactly.
+         * This may change the aspect ratio of the src.
+         */
+        kFill_ScaleToFit,
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. kStart aligns the result to the
+         * left and top edges of dst.
+         */
+        kStart_ScaleToFit,
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. The result is centered inside dst.
+         */
+        kCenter_ScaleToFit,
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. kEnd aligns the result to the
+         * right and bottom edges of dst.
+         */
+        kEnd_ScaleToFit
+    };
+
+    /** Set the matrix to the scale and translate values that map the source
+        rectangle to the destination rectangle, returning true if the the result
+        can be represented.
+        @param src the source rectangle to map from.
+        @param dst the destination rectangle to map to.
+        @param stf the ScaleToFit option
+        @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
+        @param dst  The array of dst points
+        @param count The number of points to use for the transformation
+        @return true if the matrix was set to the specified transformation
+    */
+    bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count);
+
+    /** If this matrix can be inverted, return true and if inverse is not null,
+        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;
+
+    /** Fills the passed array with affine identity values
+        in column major order.
+        @param affine  The array to fill with affine identity values.
+        Must not be NULL.
+    */
+    static void SetAffineIdentity(SkScalar affine[6]);
+
+    /** Fills the passed array with the affine values in column major order.
+        If the matrix is a perspective transform, returns false
+        and does not change the passed array.
+        @param affine  The array to fill with affine values. Ignored if NULL.
+    */
+    bool asAffine(SkScalar affine[6]) const;
+
+    /** Apply this matrix to the array of points specified by src, and write
+        the transformed points into the array of points specified by dst.
+        dst[] = M * src[]
+        @param dst  Where the transformed coordinates are written. It must
+                    contain at least count entries
+        @param src  The original coordinates that are to be transformed. It
+                    must contain at least count entries
+        @param count The number of points in src to read, and then transform
+                     into dst.
+    */
+    void mapPoints(SkPoint dst[], const SkPoint src[], int count) const;
+
+    /** Apply this matrix to the array of points, overwriting it with the
+        transformed values.
+        dst[] = M * pts[]
+        @param pts  The points to be transformed. It must contain at least
+                    count entries
+        @param count The number of points in pts.
+    */
+    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).
+     */
+    void mapPointsWithStride(SkPoint pts[], size_t stride, int count) const {
+        SkASSERT(stride >= sizeof(SkPoint));
+        SkASSERT(0 == stride % sizeof(SkScalar));
+        for (int i = 0; i < count; ++i) {
+            this->mapPoints(pts, pts, 1);
+            pts = (SkPoint*)((intptr_t)pts + stride);
+        }
+    }
+
+    /** Like mapPoints but with custom byte stride between the points.
+    */
+    void mapPointsWithStride(SkPoint dst[], SkPoint src[],
+                             size_t stride, int count) const {
+        SkASSERT(stride >= sizeof(SkPoint));
+        SkASSERT(0 == stride % sizeof(SkScalar));
+        for (int i = 0; i < count; ++i) {
+            this->mapPoints(dst, src, 1);
+            src = (SkPoint*)((intptr_t)src + stride);
+            dst = (SkPoint*)((intptr_t)dst + stride);
+        }
+    }
+
+    void mapXY(SkScalar x, SkScalar y, SkPoint* result) const {
+        SkASSERT(result);
+        this->getMapXYProc()(*this, x, y, result);
+    }
+
+    /** Apply this matrix to the array of vectors specified by src, and write
+        the transformed vectors into the array of vectors specified by dst.
+        This is similar to mapPoints, but ignores any translation in the matrix.
+        @param dst  Where the transformed coordinates are written. It must
+                    contain at least count entries
+        @param src  The original coordinates that are to be transformed. It
+                    must contain at least count entries
+        @param count The number of vectors in src to read, and then transform
+                     into dst.
+    */
+    void mapVectors(SkVector dst[], const SkVector src[], int count) const;
+
+    /** Apply this matrix to the array of vectors specified by src, and write
+        the transformed vectors into the array of vectors specified by dst.
+        This is similar to mapPoints, but ignores any translation in the matrix.
+        @param vecs The vectors to be transformed. It must contain at least
+                    count entries
+        @param count The number of vectors in vecs.
+    */
+    void mapVectors(SkVector vecs[], int count) const {
+        this->mapVectors(vecs, vecs, count);
+    }
+
+    /** Apply this matrix to the src rectangle, and write the transformed
+        rectangle into dst. This is accomplished by transforming the 4 corners
+        of src, and then setting dst to the bounds of those points.
+        @param dst  Where the transformed rectangle is written.
+        @param src  The original rectangle to be transformed.
+        @return the result of calling rectStaysRect()
+    */
+    bool mapRect(SkRect* dst, const SkRect& src) const;
+
+    /** Apply this matrix to the rectangle, and write the transformed rectangle
+        back into it. This is accomplished by transforming the 4 corners of
+        rect, and then setting it to the bounds of those points
+        @param rect The rectangle to transform.
+        @return the result of calling rectStaysRect()
+    */
+    bool mapRect(SkRect* rect) const {
+        return this->mapRect(rect, *rect);
+    }
+
+    /** Return the mean radius of a circle after it has been mapped by
+        this matrix. NOTE: in perspective this value assumes the circle
+        has its center at the origin.
+    */
+    SkScalar mapRadius(SkScalar radius) const;
+
+    typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y,
+                                 SkPoint* result);
+
+    static MapXYProc GetMapXYProc(TypeMask mask) {
+        SkASSERT((mask & ~kAllMasks) == 0);
+        return gMapXYProcs[mask & kAllMasks];
+    }
+    
+    MapXYProc getMapXYProc() const {
+        return GetMapXYProc(this->getType());
+    }
+
+    typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[],
+                                  const SkPoint src[], int count);
+
+    static MapPtsProc GetMapPtsProc(TypeMask mask) {
+        SkASSERT((mask & ~kAllMasks) == 0);
+        return gMapPtsProcs[mask & kAllMasks];
+    }
+    
+    MapPtsProc getMapPtsProc() const {
+        return GetMapPtsProc(this->getType());
+    }
+
+    /** If the matrix can be stepped in X (not complex perspective)
+        then return true and if step[XY] is not null, return the step[XY] value.
+        If it cannot, return false and ignore step.
+    */
+    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;
+    }
+
+    friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
+        return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0;
+    }
+#else
+    friend bool operator==(const SkMatrix& a, const SkMatrix& b);    
+    friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
+        return !(a == b);
+    }
+#endif
+
+    enum {
+        // flatten/unflatten 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;
+    // return the number of bytes read
+    uint32_t unflatten(const void* buffer);
+    
+    void dump() const;
+    void toDumpString(SkString*) const;
+
+    /**
+     * Calculates the maximum stretching factor of the matrix. If the matrix has
+     * perspective -1 is returned.
+     *
+     * @return maximum strecthing factor
+     */
+    SkScalar getMaxStretch() const;
+
+    /**
+     *  Return a reference to a const identity matrix
+     */
+    static const SkMatrix& I();
+
+    /**
+     *  Return a reference to a const matrix that is "invalid", one that could
+     *  never be used.
+     */
+    static const SkMatrix& InvalidMatrix();
+
+    /**
+     * Testing routine; the matrix's type cache should never need to be
+     * manually invalidated during normal use.
+     */
+    void dirtyMatrixTypeCache() {
+        this->setTypeMask(kUnknown_Mask);
+    }
+
+private:
+    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.
+             
+            This bit will be set on identity matrices
+        */
+        kRectStaysRect_Mask = 0x10,
+
+        /** Set if the perspective bit is valid even though the rest of
+            the matrix is Unknown.
+        */
+        kOnlyPerspectiveValid_Mask = 0x40,
+
+        kUnknown_Mask = 0x80,
+
+        kORableMasks =  kTranslate_Mask |
+                        kScale_Mask |
+                        kAffine_Mask |
+                        kPerspective_Mask,
+
+        kAllMasks = kTranslate_Mask |
+                    kScale_Mask |
+                    kAffine_Mask |
+                    kPerspective_Mask |
+                    kRectStaysRect_Mask
+    };
+
+    SkScalar        fMat[9];
+    mutable uint8_t fTypeMask;
+
+    uint8_t computeTypeMask() const;
+    uint8_t computePerspectiveTypeMask() const;
+
+    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);
+        fTypeMask = SkToU8(mask);
+    }
+
+    void orTypeMask(int mask) {
+        SkASSERT((mask & kORableMasks) == mask);
+        fTypeMask = SkToU8(fTypeMask | mask);
+    }
+
+    void clearTypeMask(int mask) {
+        // only allow a valid mask
+        SkASSERT((mask & kAllMasks) == mask);
+        fTypeMask &= ~mask;
+    }
+
+    TypeMask getPerspectiveTypeMaskOnly() const {
+        if ((fTypeMask & kUnknown_Mask) &&
+            !(fTypeMask & kOnlyPerspectiveValid_Mask)) {
+            fTypeMask = this->computePerspectiveTypeMask();
+        }
+        return (TypeMask)(fTypeMask & 0xF);
+    }
+
+    /** Returns true if we already know that the matrix is identity;
+        false otherwise.
+    */
+    bool isTriviallyIdentity() const {
+        if (fTypeMask & kUnknown_Mask) {
+            return false;
+        }
+        return ((fTypeMask & 0xF) == 0);
+    }
+    
+    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);
+
+    static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    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);
+    static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
+                               int count);
+    static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
+    static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
+                             int count);
+    static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
+    
+    static const MapPtsProc gMapPtsProcs[];
+
+    friend class SkPerspIter;
+};
+
+#endif
diff --git a/legacy/include/core/SkMetaData.h b/legacy/include/core/SkMetaData.h
new file mode 100644
index 0000000..86f186f
--- /dev/null
+++ b/legacy/include/core/SkMetaData.h
@@ -0,0 +1,176 @@
+
+/*
+ * 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 SkMetaData_DEFINED
+#define SkMetaData_DEFINED
+
+#include "SkScalar.h"
+
+class SkRefCnt;
+
+class SK_API SkMetaData {
+public:
+    /**
+     *  Used to manage the life-cycle of a ptr in the metadata. This is option
+     *  in setPtr, and is only invoked when either copying one metadata to
+     *  another, or when the metadata is destroyed.
+     *
+     *  setPtr(name, ptr, proc) {
+     *      fPtr = proc(ptr, true);
+     *  }
+     *
+     *  copy: A = B {
+     *      A.fPtr = B.fProc(B.fPtr, true);
+     *  }
+     *
+     *  ~SkMetaData {
+     *      fProc(fPtr, false);
+     *  }
+     */
+    typedef void* (*PtrProc)(void* ptr, bool doRef);
+
+    /**
+     *  Implements PtrProc for SkRefCnt pointers
+     */
+    static void* RefCntProc(void* ptr, bool doRef);
+
+    SkMetaData();
+    SkMetaData(const SkMetaData& src);
+    ~SkMetaData();
+
+    SkMetaData& operator=(const SkMetaData& src);
+
+    void reset();
+
+    bool findS32(const char name[], int32_t* value = NULL) const;
+    bool findScalar(const char name[], SkScalar* value = NULL) const;
+    const SkScalar* findScalars(const char name[], int* count,
+                                SkScalar values[] = NULL) const;
+    const char* findString(const char name[]) const;
+    bool findPtr(const char name[], void** value = NULL, PtrProc* = NULL) const;
+    bool findBool(const char name[], bool* value = NULL) const;
+    const void* findData(const char name[], size_t* byteCount = NULL) const;
+
+    bool hasS32(const char name[], int32_t value) const {
+        int32_t v;
+        return this->findS32(name, &v) && v == value;
+    }
+    bool hasScalar(const char name[], SkScalar value) const {
+        SkScalar v;
+        return this->findScalar(name, &v) && v == value;
+    }
+    bool hasString(const char name[], const char value[]) const {
+        const char* v = this->findString(name);
+        return  (v == NULL && value == NULL) ||
+                (v != NULL && value != NULL && !strcmp(v, value));
+    }
+    bool hasPtr(const char name[], void* value) const {
+        void* v;
+        return this->findPtr(name, &v) && v == value;
+    }
+    bool hasBool(const char name[], bool value) const {
+        bool    v;
+        return this->findBool(name, &v) && v == value;
+    }
+    bool hasData(const char name[], const void* data, size_t byteCount) const {
+        size_t len;
+        const void* ptr = this->findData(name, &len);
+        return NULL != ptr && len == byteCount && !memcmp(ptr, data, len);
+    }
+
+    void setS32(const char name[], int32_t value);
+    void setScalar(const char name[], SkScalar value);
+    SkScalar* setScalars(const char name[], int count, const SkScalar values[] = NULL);
+    void setString(const char name[], const char value[]);
+    void setPtr(const char name[], void* value, PtrProc proc = NULL);
+    void setBool(const char name[], bool value);
+    // the data is copied from the input pointer.
+    void setData(const char name[], const void* data, size_t byteCount);
+
+    bool removeS32(const char name[]);
+    bool removeScalar(const char name[]);
+    bool removeString(const char name[]);
+    bool removePtr(const char name[]);
+    bool removeBool(const char name[]);
+    bool removeData(const char name[]);
+
+    // helpers for SkRefCnt
+    bool findRefCnt(const char name[], SkRefCnt** ptr = NULL) {
+        return this->findPtr(name, reinterpret_cast<void**>(ptr));
+    }
+    bool hasRefCnt(const char name[], SkRefCnt* ptr) {
+        return this->hasPtr(name, ptr);
+    }
+    void setRefCnt(const char name[], SkRefCnt* ptr) {
+        this->setPtr(name, ptr, RefCntProc);
+    }
+    bool removeRefCnt(const char name[]) {
+        return this->removePtr(name);
+    }
+
+    enum Type {
+        kS32_Type,
+        kScalar_Type,
+        kString_Type,
+        kPtr_Type,
+        kBool_Type,
+        kData_Type,
+
+        kTypeCount
+    };
+
+    struct Rec;
+    class Iter;
+    friend class Iter;
+
+    class Iter {
+    public:
+        Iter() : fRec(NULL) {}
+        Iter(const SkMetaData&);
+
+        /** Reset the iterator, so that calling next() will return the first
+            data element. This is done implicitly in the constructor.
+        */
+        void reset(const SkMetaData&);
+
+        /** Each time next is called, it returns the name of the next data element,
+            or null when there are no more elements. If non-null is returned, then the
+            element's type is returned (if not null), and the number of data values
+            is returned in count (if not null).
+        */
+        const char* next(Type*, int* count);
+
+    private:
+        Rec* fRec;
+    };
+
+public:
+    struct Rec {
+        Rec*        fNext;
+        uint16_t    fDataCount; // number of elements
+        uint8_t     fDataLen;   // sizeof a single element
+        uint8_t     fType;
+
+        const void* data() const { return (this + 1); }
+        void*       data() { return (this + 1); }
+        const char* name() const { return (const char*)this->data() + fDataLen * fDataCount; }
+        char*       name() { return (char*)this->data() + fDataLen * fDataCount; }
+
+        static Rec* Alloc(size_t);
+        static void Free(Rec*);
+    };
+    Rec*    fRec;
+
+    const Rec* find(const char name[], Type) const;
+    void* set(const char name[], const void* data, size_t len, Type, int count);
+    bool remove(const char name[], Type);
+};
+
+#endif
+
diff --git a/legacy/include/core/SkOSFile.h b/legacy/include/core/SkOSFile.h
new file mode 100644
index 0000000..9f01ead
--- /dev/null
+++ b/legacy/include/core/SkOSFile.h
@@ -0,0 +1,82 @@
+
+/*
+ * 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 SkOSFile_DEFINED
+#define SkOSFile_DEFINED
+
+#include "SkString.h"
+
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+    #include <dirent.h>
+#endif
+
+struct SkFILE;
+
+enum SkFILE_Flags {
+    kRead_SkFILE_Flag   = 0x01,
+    kWrite_SkFILE_Flag  = 0x02
+};
+
+SkFILE* sk_fopen(const char path[], SkFILE_Flags);
+void    sk_fclose(SkFILE*);
+
+size_t  sk_fgetsize(SkFILE*);
+/** Return true if the file could seek back to the beginning
+*/
+bool    sk_frewind(SkFILE*);
+
+size_t  sk_fread(void* buffer, size_t byteCount, SkFILE*);
+size_t  sk_fwrite(const void* buffer, size_t byteCount, SkFILE*);
+void    sk_fflush(SkFILE*);
+
+int     sk_fseek( SkFILE*, size_t, int );
+size_t  sk_ftell( SkFILE* );
+
+class SkOSFile {
+public:
+    class Iter {
+    public:
+        Iter();
+        Iter(const char path[], const char suffix[] = NULL);
+        ~Iter();
+
+        void reset(const char path[], const char suffix[] = NULL);
+        /** If getDir is true, only returns directories.
+            Results are undefined if true and false calls are
+            interleaved on a single iterator.
+        */
+        bool next(SkString* name, bool getDir = false);
+
+    private:
+#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)
+        DIR*        fDIR;
+        SkString    fPath, fSuffix;
+#endif
+    };
+};
+
+class SkUTF16_Str {
+public:
+    SkUTF16_Str(const char src[]);
+    ~SkUTF16_Str()
+    {
+        sk_free(fStr);
+    }
+    const uint16_t* get() const { return fStr; }
+
+private:
+    uint16_t*   fStr;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkPackBits.h b/legacy/include/core/SkPackBits.h
new file mode 100644
index 0000000..f0614a0
--- /dev/null
+++ b/legacy/include/core/SkPackBits.h
@@ -0,0 +1,79 @@
+
+/*
+ * 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 SkPackBits_DEFINED
+#define SkPackBits_DEFINED
+
+#include "SkTypes.h"
+
+class SkPackBits {
+public:
+    /** Given the number of 16bit values that will be passed to Pack16,
+        returns the worst-case size needed for the dst[] buffer.
+    */
+    static size_t ComputeMaxSize16(int count);
+
+    /** Given the number of 8bit values that will be passed to Pack8,
+        returns the worst-case size needed for the dst[] buffer.
+    */
+    static size_t ComputeMaxSize8(int count);
+
+    /** Write the src array into a packed format. The packing process may end
+        up writing more bytes than it read, so dst[] must be large enough.
+        @param src      Input array of 16bit values
+        @param count    Number of entries in src[]
+        @param dst      Buffer (allocated by caller) to write the packed data
+                        into
+        @return the number of bytes written to dst[]
+    */
+    static size_t Pack16(const uint16_t src[], int count, uint8_t dst[]);
+
+    /** Write the src array into a packed format. The packing process may end
+        up writing more bytes than it read, so dst[] must be large enough.
+        @param src      Input array of 8bit values
+        @param count    Number of entries in src[]
+        @param dst      Buffer (allocated by caller) to write the packed data
+                        into
+        @return the number of bytes written to dst[]
+    */
+    static size_t Pack8(const uint8_t src[], int count, uint8_t dst[]);
+
+    /** Unpack the data in src[], and expand it into dst[]. The src[] data was
+        written by a previous call to Pack16.
+        @param src  Input data to unpack, previously created by Pack16.
+        @param srcSize  Number of bytes of src to unpack
+        @param dst  Buffer (allocated by caller) to expand the src[] into.
+        @return the number of dst elements (not bytes) written into dst.
+    */
+    static int Unpack16(const uint8_t src[], size_t srcSize, uint16_t dst[]);
+
+    /** Unpack the data in src[], and expand it into dst[]. The src[] data was
+        written by a previous call to Pack8.
+        @param src      Input data to unpack, previously created by Pack8.
+        @param srcSize  Number of bytes of src to unpack
+        @param dst      Buffer (allocated by caller) to expand the src[] into.
+        @return the number of bytes written into dst.
+    */
+    static int Unpack8(const uint8_t src[], size_t srcSize, uint8_t dst[]);
+
+    /** Unpack the data from src[], skip the first dstSkip bytes, then write
+        dstWrite bytes into dst[]. The src[] data was written by a previous
+        call to Pack8. Return the number of bytes actually writtten into dst[]
+        @param src      Input data to unpack, previously created by Pack8.
+        @param dst      Buffer (allocated by caller) to expand the src[] into.
+        @param dstSkip  Number of bytes of unpacked src to skip before writing
+                        into dst
+        @param dstWrite Number of bytes of unpacked src to write into dst (after
+                        skipping dstSkip bytes)
+    */
+    static void Unpack8(uint8_t dst[], size_t dstSkip, size_t dstWrite,
+                        const uint8_t src[]);
+};
+
+#endif
diff --git a/legacy/include/core/SkPaint.h b/legacy/include/core/SkPaint.h
new file mode 100644
index 0000000..8e46deb
--- /dev/null
+++ b/legacy/include/core/SkPaint.h
@@ -0,0 +1,1007 @@
+
+/*
+ * 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 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 SkAutoGlyphCache;
+class SkColorFilter;
+class SkDescriptor;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+struct SkGlyph;
+struct SkRect;
+class SkGlyphCache;
+class SkImageFilter;
+class SkMaskFilter;
+class SkMatrix;
+class SkPath;
+class SkPathEffect;
+class SkRasterizer;
+class SkShader;
+class SkTypeface;
+
+typedef const SkGlyph& (*SkDrawCacheProc)(SkGlyphCache*, const char**,
+                                           SkFixed x, SkFixed y);
+
+typedef const SkGlyph& (*SkMeasureCacheProc)(SkGlyphCache*, const char**);
+
+/** \class SkPaint
+
+    The SkPaint class holds the style and color information about how to draw
+    geometries, text and bitmaps.
+*/
+class SK_API SkPaint {
+public:
+    SkPaint();
+    SkPaint(const SkPaint& paint);
+    ~SkPaint();
+
+    SkPaint& operator=(const SkPaint&);
+
+    SK_API friend bool operator==(const SkPaint& a, const SkPaint& b);
+    friend bool operator!=(const SkPaint& a, const SkPaint& b) {
+        return !(a == b);
+    }
+
+    void flatten(SkFlattenableWriteBuffer&) const;
+    void unflatten(SkFlattenableReadBuffer&);
+
+    /** Restores the paint to its initial settings.
+    */
+    void reset();
+
+    /** Specifies the level of hinting to be performed. These names are taken
+        from the Gnome/Cairo names for the same. They are translated into
+        Freetype concepts the same as in cairo-ft-font.c:
+           kNo_Hinting     -> FT_LOAD_NO_HINTING
+           kSlight_Hinting -> FT_LOAD_TARGET_LIGHT
+           kNormal_Hinting -> <default, no option>
+           kFull_Hinting   -> <same as kNormalHinting, unless we are rendering
+                              subpixel glyphs, in which case TARGET_LCD or
+                              TARGET_LCD_V is used>
+    */
+    enum Hinting {
+        kNo_Hinting            = 0,
+        kSlight_Hinting        = 1,
+        kNormal_Hinting        = 2,     //!< this is the default
+        kFull_Hinting          = 3,
+    };
+
+    Hinting getHinting() const {
+        return static_cast<Hinting>(fHinting);
+    }
+
+    void setHinting(Hinting hintingLevel);
+
+    /** Specifies the bit values that are stored in the paint's flags.
+    */
+    enum Flags {
+        kAntiAlias_Flag       = 0x01,   //!< mask to enable antialiasing
+        kFilterBitmap_Flag    = 0x02,   //!< mask to enable bitmap filtering
+        kDither_Flag          = 0x04,   //!< mask to enable dithering
+        kUnderlineText_Flag   = 0x08,   //!< mask to enable underline text
+        kStrikeThruText_Flag  = 0x10,   //!< mask to enable strike-thru text
+        kFakeBoldText_Flag    = 0x20,   //!< mask to enable fake-bold text
+        kLinearText_Flag      = 0x40,   //!< mask to enable linear-text
+        kSubpixelText_Flag    = 0x80,   //!< mask to enable subpixel text positioning
+        kDevKernText_Flag     = 0x100,  //!< mask to enable device kerning text
+        kLCDRenderText_Flag   = 0x200,  //!< mask to enable subpixel glyph renderering
+        kEmbeddedBitmapText_Flag = 0x400, //!< mask to enable embedded bitmap strikes
+        kAutoHinting_Flag     = 0x800,  //!< mask to force Freetype's autohinter
+        kVerticalText_Flag    = 0x1000,
+        kGenA8FromLCD_Flag    = 0x2000, // hack for GDI -- do not use if you can help it
+
+        // when adding extra flags, note that the fFlags member is specified
+        // with a bit-width and you'll have to expand it.
+
+        kAllFlags = 0x3FFF
+    };
+
+    /** Return the paint's flags. Use the Flag enum to test flag values.
+        @return the paint's flags (see enums ending in _Flag for bit masks)
+    */
+    uint32_t getFlags() const { return fFlags; }
+
+    /** Set the paint's flags. Use the Flag enum to specific flag values.
+        @param flags    The new flag bits for the paint (see Flags enum)
+    */
+    void setFlags(uint32_t flags);
+
+    /** Helper for getFlags(), returning true if kAntiAlias_Flag bit is set
+        @return true if the antialias bit is set in the paint's flags.
+        */
+    bool isAntiAlias() const {
+        return SkToBool(this->getFlags() & kAntiAlias_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kAntiAlias_Flag bit
+        @param aa   true to enable antialiasing, false to disable it
+        */
+    void setAntiAlias(bool aa);
+
+    /** Helper for getFlags(), returning true if kDither_Flag bit is set
+        @return true if the dithering bit is set in the paint's flags.
+        */
+    bool isDither() const {
+        return SkToBool(this->getFlags() & kDither_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kDither_Flag bit
+        @param dither   true to enable dithering, false to disable it
+        */
+    void setDither(bool dither);
+
+    /** Helper for getFlags(), returning true if kLinearText_Flag bit is set
+        @return true if the lineartext bit is set in the paint's flags
+    */
+    bool isLinearText() const {
+        return SkToBool(this->getFlags() & kLinearText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kLinearText_Flag bit
+        @param linearText true to set the linearText bit in the paint's flags,
+                          false to clear it.
+    */
+    void setLinearText(bool linearText);
+
+    /** Helper for getFlags(), returning true if kSubpixelText_Flag bit is set
+        @return true if the lineartext bit is set in the paint's flags
+    */
+    bool isSubpixelText() const {
+        return SkToBool(this->getFlags() & kSubpixelText_Flag);
+    }
+
+    /**
+     *  Helper for setFlags(), setting or clearing the kSubpixelText_Flag.
+     *  @param subpixelText true to set the subpixelText bit in the paint's
+     *                      flags, false to clear it.
+     */
+    void setSubpixelText(bool subpixelText);
+
+    bool isLCDRenderText() const {
+        return SkToBool(this->getFlags() & kLCDRenderText_Flag);
+    }
+
+    /**
+     *  Helper for setFlags(), setting or clearing the kLCDRenderText_Flag.
+     *  Note: antialiasing must also be on for lcd rendering
+     *  @param lcdText true to set the LCDRenderText bit in the paint's flags,
+     *                 false to clear it.
+     */
+    void setLCDRenderText(bool lcdText);
+
+    bool isEmbeddedBitmapText() const {
+        return SkToBool(this->getFlags() & kEmbeddedBitmapText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kEmbeddedBitmapText_Flag bit
+        @param useEmbeddedBitmapText true to set the kEmbeddedBitmapText bit in the paint's flags,
+                                     false to clear it.
+    */
+    void setEmbeddedBitmapText(bool useEmbeddedBitmapText);
+
+    bool isAutohinted() const {
+        return SkToBool(this->getFlags() & kAutoHinting_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kAutoHinting_Flag bit
+        @param useAutohinter true to set the kEmbeddedBitmapText bit in the
+                                  paint's flags,
+                             false to clear it.
+    */
+    void setAutohinted(bool useAutohinter);
+
+    bool isVerticalText() const {
+        return SkToBool(this->getFlags() & kVerticalText_Flag);
+    }
+    
+    /**
+     *  Helper for setting or clearing the kVerticalText_Flag bit in
+     *  setFlags(...).
+     *
+     *  If this bit is set, then advances are treated as Y values rather than
+     *  X values, and drawText will places its glyphs vertically rather than
+     *  horizontally.
+     */
+    void setVerticalText(bool);
+
+    /** Helper for getFlags(), returning true if kUnderlineText_Flag bit is set
+        @return true if the underlineText bit is set in the paint's flags.
+    */
+    bool isUnderlineText() const {
+        return SkToBool(this->getFlags() & kUnderlineText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kUnderlineText_Flag bit
+        @param underlineText true to set the underlineText bit in the paint's
+                             flags, false to clear it.
+    */
+    void setUnderlineText(bool underlineText);
+
+    /** Helper for getFlags(), returns true if kStrikeThruText_Flag bit is set
+        @return true if the strikeThruText bit is set in the paint's flags.
+    */
+    bool isStrikeThruText() const {
+        return SkToBool(this->getFlags() & kStrikeThruText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kStrikeThruText_Flag bit
+        @param strikeThruText   true to set the strikeThruText bit in the
+                                paint's flags, false to clear it.
+    */
+    void setStrikeThruText(bool strikeThruText);
+
+    /** Helper for getFlags(), returns true if kFakeBoldText_Flag bit is set
+        @return true if the kFakeBoldText_Flag bit is set in the paint's flags.
+    */
+    bool isFakeBoldText() const {
+        return SkToBool(this->getFlags() & kFakeBoldText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kFakeBoldText_Flag bit
+        @param fakeBoldText true to set the kFakeBoldText_Flag bit in the paint's
+                            flags, false to clear it.
+    */
+    void setFakeBoldText(bool fakeBoldText);
+
+    /** Helper for getFlags(), returns true if kDevKernText_Flag bit is set
+        @return true if the kernText bit is set in the paint's flags.
+    */
+    bool isDevKernText() const {
+        return SkToBool(this->getFlags() & kDevKernText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kKernText_Flag bit
+        @param kernText true to set the kKernText_Flag bit in the paint's
+                            flags, false to clear it.
+    */
+    void setDevKernText(bool devKernText);
+
+    bool isFilterBitmap() const {
+        return SkToBool(this->getFlags() & kFilterBitmap_Flag);
+    }
+
+    void setFilterBitmap(bool filterBitmap);
+
+    /** Styles apply to rect, oval, path, and text.
+        Bitmaps are always drawn in "fill", and lines are always drawn in
+        "stroke".
+
+        Note: strokeandfill implicitly draws the result with
+        SkPath::kWinding_FillType, so if the original path is even-odd, the
+        results may not appear the same as if it was drawn twice, filled and
+        then stroked.
+    */
+    enum Style {
+        kFill_Style,            //!< fill the geometry
+        kStroke_Style,          //!< stroke the geometry
+        kStrokeAndFill_Style,   //!< fill and stroke the geometry
+
+        kStyleCount,
+    };
+
+    /** Return the paint's style, used for controlling how primitives'
+        geometries are interpreted (except for drawBitmap, which always assumes
+        kFill_Style).
+        @return the paint's Style
+    */
+    Style getStyle() const { return (Style)fStyle; }
+
+    /** Set the paint's style, used for controlling how primitives'
+        geometries are interpreted (except for drawBitmap, which always assumes
+        Fill).
+        @param style    The new style to set in the paint
+    */
+    void setStyle(Style style);
+
+    /** Return the paint's color. Note that the color is a 32bit value
+        containing alpha as well as r,g,b. This 32bit value is not
+        premultiplied, meaning that its alpha can be any value, regardless of
+        the values of r,g,b.
+        @return the paint's color (and alpha).
+    */
+    SkColor getColor() const { return fColor; }
+
+    /** Set the paint's color. Note that the color is a 32bit value containing
+        alpha as well as r,g,b. This 32bit value is not premultiplied, meaning
+        that its alpha can be any value, regardless of the values of r,g,b.
+        @param color    The new color (including alpha) to set in the paint.
+    */
+    void setColor(SkColor color);
+
+    /** Helper to getColor() that just returns the color's alpha value.
+        @return the alpha component of the paint's color.
+        */
+    uint8_t getAlpha() const { return SkToU8(SkColorGetA(fColor)); }
+
+    /** Helper to setColor(), that only assigns the color's alpha value,
+        leaving its r,g,b values unchanged.
+        @param a    set the alpha component (0..255) of the paint's color.
+    */
+    void setAlpha(U8CPU a);
+
+    /** Helper to setColor(), that takes a,r,g,b and constructs the color value
+        using SkColorSetARGB()
+        @param a    The new alpha component (0..255) of the paint's color.
+        @param r    The new red component (0..255) of the paint's color.
+        @param g    The new green component (0..255) of the paint's color.
+        @param b    The new blue component (0..255) of the paint's color.
+    */
+    void setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+
+    /** Return the width for stroking.
+        <p />
+        A value of 0 strokes in hairline mode.
+        Hairlines always draw 1-pixel wide, regardless of the matrix.
+        @return the paint's stroke width, used whenever the paint's style is
+                Stroke or StrokeAndFill.
+    */
+    SkScalar getStrokeWidth() const { return fWidth; }
+
+    /** Set the width for stroking.
+        Pass 0 to stroke in hairline mode.
+        Hairlines always draw 1-pixel wide, regardless of the matrix.
+        @param width set the paint's stroke width, used whenever the paint's
+                     style is Stroke or StrokeAndFill.
+    */
+    void setStrokeWidth(SkScalar width);
+
+    /** Return the paint's stroke miter value. This is used to control the
+        behavior of miter joins when the joins angle is sharp.
+        @return the paint's miter limit, used whenever the paint's style is
+                Stroke or StrokeAndFill.
+    */
+    SkScalar getStrokeMiter() const { return fMiterLimit; }
+
+    /** Set the paint's stroke miter value. This is used to control the
+        behavior of miter joins when the joins angle is sharp. This value must
+        be >= 0.
+        @param miter    set the miter limit on the paint, used whenever the
+                        paint's style is Stroke or StrokeAndFill.
+    */
+    void setStrokeMiter(SkScalar miter);
+
+    /** Cap enum specifies the settings for the paint's strokecap. This is the
+        treatment that is applied to the beginning and end of each non-closed
+        contour (e.g. lines).
+    */
+    enum Cap {
+        kButt_Cap,      //!< begin/end contours with no extension
+        kRound_Cap,     //!< begin/end contours with a semi-circle extension
+        kSquare_Cap,    //!< begin/end contours with a half square extension
+
+        kCapCount,
+        kDefault_Cap = kButt_Cap
+    };
+
+    /** Join enum specifies the settings for the paint's strokejoin. This is
+        the treatment that is applied to corners in paths and rectangles.
+    */
+    enum Join {
+        kMiter_Join,    //!< connect path segments with a sharp join
+        kRound_Join,    //!< connect path segments with a round join
+        kBevel_Join,    //!< connect path segments with a flat bevel join
+
+        kJoinCount,
+        kDefault_Join = kMiter_Join
+    };
+
+    /** Return the paint's stroke cap type, controlling how the start and end
+        of stroked lines and paths are treated.
+        @return the line cap style for the paint, used whenever the paint's
+                style is Stroke or StrokeAndFill.
+    */
+    Cap getStrokeCap() const { return (Cap)fCapType; }
+
+    /** Set the paint's stroke cap type.
+        @param cap  set the paint's line cap style, used whenever the paint's
+                    style is Stroke or StrokeAndFill.
+    */
+    void setStrokeCap(Cap cap);
+
+    /** Return the paint's stroke join type.
+        @return the paint's line join style, used whenever the paint's style is
+                Stroke or StrokeAndFill.
+    */
+    Join getStrokeJoin() const { return (Join)fJoinType; }
+
+    /** Set the paint's stroke join type.
+        @param join set the paint's line join style, used whenever the paint's
+                    style is Stroke or StrokeAndFill.
+    */
+    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);
+    }
+
+    /** Get the paint's shader object.
+        <p />
+      The shader's reference count is not affected.
+        @return the paint's shader (or NULL)
+    */
+    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
+    */
+    SkShader* setShader(SkShader* shader);
+
+    /** Get the paint's colorfilter. If there is a colorfilter, its reference
+        count is not changed.
+        @return the paint's colorfilter (or NULL)
+    */
+    SkColorFilter* getColorFilter() const { return fColorFilter; }
+
+    /** Set or clear the paint's colorfilter, returning the parameter.
+        <p />
+        If the paint already has a filter, its reference count is decremented.
+        If filter is not NULL, its reference count is incremented.
+        @param filter   May be NULL. The filter to be installed in the paint
+        @return         filter
+    */
+    SkColorFilter* setColorFilter(SkColorFilter* filter);
+
+    /** Get the paint's xfermode object.
+        <p />
+      The xfermode's reference count is not affected.
+        @return the paint's xfermode (or NULL)
+    */
+    SkXfermode* getXfermode() const { return fXfermode; }
+
+    /** Set or clear the xfermode object.
+        <p />
+        Pass NULL to clear any previous xfermode.
+        As a convenience, the parameter passed is also returned.
+        If a previous xfermode exists, its reference count is decremented.
+        If xfermode is not NULL, its reference count is incremented.
+        @param xfermode May be NULL. The new xfermode to be installed in the
+                        paint
+        @return         xfermode
+    */
+    SkXfermode* setXfermode(SkXfermode* xfermode);
+
+    /** Create an xfermode based on the specified Mode, and assign it into the
+        paint, returning the mode that was set. If the Mode is SrcOver, then
+        the paint's xfermode is set to null.
+     */
+    SkXfermode* setXfermodeMode(SkXfermode::Mode);
+
+    /** Get the paint's patheffect object.
+        <p />
+      The patheffect reference count is not affected.
+        @return the paint's patheffect (or NULL)
+    */
+    SkPathEffect* getPathEffect() const { return fPathEffect; }
+
+    /** Set or clear the patheffect object.
+        <p />
+        Pass NULL to clear any previous patheffect.
+        As a convenience, the parameter passed is also returned.
+        If a previous patheffect exists, its reference count is decremented.
+        If patheffect is not NULL, its reference count is incremented.
+        @param effect   May be NULL. The new patheffect to be installed in the
+                        paint
+        @return         effect
+    */
+    SkPathEffect* setPathEffect(SkPathEffect* effect);
+
+    /** Get the paint's maskfilter object.
+        <p />
+      The maskfilter reference count is not affected.
+        @return the paint's maskfilter (or NULL)
+    */
+    SkMaskFilter* getMaskFilter() const { return fMaskFilter; }
+
+    /** Set or clear the maskfilter object.
+        <p />
+        Pass NULL to clear any previous maskfilter.
+        As a convenience, the parameter passed is also returned.
+        If a previous maskfilter exists, its reference count is decremented.
+        If maskfilter is not NULL, its reference count is incremented.
+        @param maskfilter   May be NULL. The new maskfilter to be installed in
+                            the paint
+        @return             maskfilter
+    */
+    SkMaskFilter* setMaskFilter(SkMaskFilter* maskfilter);
+
+    // These attributes are for text/fonts
+
+    /** Get the paint's typeface object.
+        <p />
+        The typeface object identifies which font to use when drawing or
+        measuring text. The typeface reference count is not affected.
+        @return the paint's typeface (or NULL)
+    */
+    SkTypeface* getTypeface() const { return fTypeface; }
+
+    /** Set or clear the typeface object.
+        <p />
+        Pass NULL to clear any previous typeface.
+        As a convenience, the parameter passed is also returned.
+        If a previous typeface exists, its reference count is decremented.
+        If typeface is not NULL, its reference count is incremented.
+        @param typeface May be NULL. The new typeface to be installed in the
+                        paint
+        @return         typeface
+    */
+    SkTypeface* setTypeface(SkTypeface* typeface);
+
+    /** Get the paint's rasterizer (or NULL).
+        <p />
+        The raster controls how paths/text are turned into alpha masks.
+        @return the paint's rasterizer (or NULL)
+    */
+    SkRasterizer* getRasterizer() const { return fRasterizer; }
+
+    /** Set or clear the rasterizer object.
+        <p />
+        Pass NULL to clear any previous rasterizer.
+        As a convenience, the parameter passed is also returned.
+        If a previous rasterizer exists in the paint, its reference count is
+        decremented. If rasterizer is not NULL, its reference count is
+        incremented.
+        @param rasterizer May be NULL. The new rasterizer to be installed in
+                          the paint.
+        @return           rasterizer
+    */
+    SkRasterizer* setRasterizer(SkRasterizer* rasterizer);
+
+    SkImageFilter* getImageFilter() const { return fImageFilter; }
+    SkImageFilter* setImageFilter(SkImageFilter*);
+
+    /**
+     *  Return the paint's SkDrawLooper (if any). Does not affect the looper's
+     *  reference count.
+     */
+    SkDrawLooper* getLooper() const { return fLooper; }
+
+    /**
+     *  Set or clear the looper object.
+     *  <p />
+     *  Pass NULL to clear any previous looper.
+     *  As a convenience, the parameter passed is also returned.
+     *  If a previous looper exists in the paint, its reference count is
+     *  decremented. If looper is not NULL, its reference count is
+     *  incremented.
+     *  @param looper May be NULL. The new looper to be installed in the paint.
+     *  @return looper
+     */
+    SkDrawLooper* setLooper(SkDrawLooper* looper);
+
+    enum Align {
+        kLeft_Align,
+        kCenter_Align,
+        kRight_Align,
+
+        kAlignCount
+    };
+
+    /** Return the paint's Align value for drawing text.
+        @return the paint's Align value for drawing text.
+    */
+    Align   getTextAlign() const { return (Align)fTextAlign; }
+
+    /** Set the paint's text alignment.
+        @param align set the paint's Align value for drawing text.
+    */
+    void    setTextAlign(Align align);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    /** Return the paint's language value used for drawing text.
+        @return the paint's language value used for drawing text.
+    */
+    const SkLanguage& getLanguage() const { return fLanguage; }
+
+    /** Set the paint's language value used for drawing text.
+        @param language set the paint's language value for drawing text.
+    */
+    void setLanguage(const SkLanguage& language);
+
+
+    enum FontVariant {
+       kDefault_Variant, // Currently setting yourself to Default gives you Compact Variant
+       kCompact_Variant,
+       kElegant_Variant,
+       kLast_Variant = kElegant_Variant,
+    };
+
+    /** Return the font variant
+        @return the font variant used by this paint object
+    */
+    FontVariant getFontVariant() const { return fFontVariant; }
+
+
+    /** Set the font variant
+      @param fontVariant set the paint's font variant for choosing fonts
+    */
+    void setFontVariant(FontVariant fontVariant);
+#endif
+
+    /** Return the paint's text size.
+        @return the paint's text size.
+    */
+    SkScalar getTextSize() const { return fTextSize; }
+
+    /** Set the paint's text size. This value must be > 0
+        @param textSize set the paint's text size.
+    */
+    void setTextSize(SkScalar textSize);
+
+    /** Return the paint's horizontal scale factor for text. The default value
+        is 1.0.
+        @return the paint's scale factor in X for drawing/measuring text
+    */
+    SkScalar getTextScaleX() const { return fTextScaleX; }
+
+    /** Set the paint's horizontal scale factor for text. The default value
+        is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
+        stretch the text narrower.
+        @param scaleX   set the paint's scale factor in X for drawing/measuring
+                        text.
+    */
+    void setTextScaleX(SkScalar scaleX);
+
+    /** Return the paint's horizontal skew factor for text. The default value
+        is 0.
+        @return the paint's skew factor in X for drawing text.
+    */
+    SkScalar getTextSkewX() const { return fTextSkewX; }
+
+    /** Set the paint's horizontal skew factor for text. The default value
+        is 0. For approximating oblique text, use values around -0.25.
+        @param skewX set the paint's skew factor in X for drawing text.
+    */
+    void setTextSkewX(SkScalar skewX);
+
+    /** 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
+        kGlyphID_TextEncoding   //!< the text parameters are glyph indices
+    };
+
+    TextEncoding getTextEncoding() const { return (TextEncoding)fTextEncoding; }
+
+    void setTextEncoding(TextEncoding encoding);
+
+    struct FontMetrics {
+        SkScalar    fTop;       //!< The greatest distance above the baseline for any glyph (will be <= 0)
+        SkScalar    fAscent;    //!< The recommended distance above the baseline (will be <= 0)
+        SkScalar    fDescent;   //!< The recommended distance below the baseline (will be >= 0)
+        SkScalar    fBottom;    //!< The greatest distance below the baseline for any glyph (will be >= 0)
+        SkScalar    fLeading;   //!< The recommended distance to add between lines of text (will be >= 0)
+        SkScalar    fAvgCharWidth;  //!< the average charactor width (>= 0)
+        SkScalar    fXMin;      //!< The minimum bounding box x value for all glyphs
+        SkScalar    fXMax;      //!< The maximum bounding box x value for all glyphs
+        SkScalar    fXHeight;   //!< the height of an 'x' in px, or 0 if no 'x' in face
+    };
+
+    /** Return the recommend spacing between lines (which will be
+        fDescent - fAscent + fLeading).
+        If metrics is not null, return in it the font metrics for the
+        typeface/pointsize/etc. currently set in the paint.
+        @param metrics      If not null, returns the font metrics for the
+                            current typeface/pointsize/etc setting in this
+                            paint.
+        @param scale        If not 0, return width as if the canvas were scaled
+                            by this value
+        @param return the recommended spacing between lines
+    */
+    SkScalar getFontMetrics(FontMetrics* metrics, SkScalar scale = 0) const;
+
+    /** Return the recommend line spacing. This will be
+        fDescent - fAscent + fLeading
+    */
+    SkScalar getFontSpacing() const { return this->getFontMetrics(NULL, 0); }
+
+    /** Convert the specified text into glyph IDs, returning the number of
+        glyphs ID written. If glyphs is NULL, it is ignore and only the count
+        is returned.
+    */
+    int textToGlyphs(const void* text, size_t byteLength,
+                     uint16_t glyphs[]) const;
+
+    /** Return true if all of the specified text has a corresponding non-zero
+        glyph ID. If any of the code-points in the text are not supported in
+        the typeface (i.e. the glyph ID would be zero), then return false.
+
+        If the text encoding for the paint is kGlyph_TextEncoding, then this
+        returns true if all of the specified glyph IDs are non-zero.
+     */
+    bool containsText(const void* text, size_t byteLength) const;
+
+    /** Convert the glyph array into Unichars. Unconvertable glyphs are mapped
+        to zero. Note: this does not look at the text-encoding setting in the
+        paint, only at the typeface.
+    */
+    void glyphsToUnichars(const uint16_t glyphs[], int count,
+                          SkUnichar text[]) const;
+
+    /** Return the number of drawable units in the specified text buffer.
+        This looks at the current TextEncoding field of the paint. If you also
+        want to have the text converted into glyph IDs, call textToGlyphs
+        instead.
+    */
+    int countText(const void* text, size_t byteLength) const {
+        return this->textToGlyphs(text, byteLength, NULL);
+    }
+
+    /** Return the width of the text. This will return the vertical measure
+     *  if isVerticalText() is true, in which case the returned value should
+     *  be treated has a height instead of a width.
+     *
+     *  @param text         The text to be measured
+     *  @param length       Number of bytes of text to measure
+     *  @param bounds       If not NULL, returns the bounds of the text,
+     *                      relative to (0, 0).
+     *  @param scale        If not 0, return width as if the canvas were scaled
+     *                      by this value
+     *  @return             The advance width of the text
+     */
+    SkScalar measureText(const void* text, size_t length,
+                         SkRect* bounds, SkScalar scale = 0) const;
+
+    /** Return the width of the text. This will return the vertical measure
+     *  if isVerticalText() is true, in which case the returned value should
+     *  be treated has a height instead of a width.
+     *
+     *  @param text     Address of the text
+     *  @param length   Number of bytes of text to measure
+     *  @return         The width of the text
+     */
+    SkScalar measureText(const void* text, size_t length) const {
+        return this->measureText(text, length, NULL, 0);
+    }
+
+    /** Specify the direction the text buffer should be processed in breakText()
+    */
+    enum TextBufferDirection {
+        /** When measuring text for breakText(), begin at the start of the text
+            buffer and proceed forward through the data. This is the default.
+        */
+        kForward_TextBufferDirection,
+        /** When measuring text for breakText(), begin at the end of the text
+            buffer and proceed backwards through the data.
+        */
+        kBackward_TextBufferDirection
+    };
+
+    /** 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
+     *                  widths are <= maxWidth are measured.
+     *  @param measuredWidth Optional. If non-null, this returns the actual
+     *                  width of the measured text.
+     *  @param tbd      Optional. The direction the text buffer should be
+     *                  traversed during measuring.
+     *  @return         The number of bytes of text that were measured. Will be
+     *                  <= length.
+     */
+    size_t  breakText(const void* text, size_t length, SkScalar maxWidth,
+                      SkScalar* measuredWidth = NULL,
+                      TextBufferDirection tbd = kForward_TextBufferDirection)
+                      const;
+
+    /** Return the advances for the text. These will be vertical advances if
+     *  isVerticalText() returns true.
+     *
+     *  @param text         the text
+     *  @param byteLength   number of bytes to of text
+     *  @param widths       If not null, returns the array of advances for
+     *                      the glyphs. If not NULL, must be at least a large
+     *                      as the number of unichars in the specified text.
+     *  @param bounds       If not null, returns the bounds for each of
+     *                      character, relative to (0, 0)
+     *  @return the number of unichars in the specified text.
+     */
+    int getTextWidths(const void* text, size_t byteLength, SkScalar widths[],
+                      SkRect bounds[] = NULL) const;
+
+    /** Return the path (outline) for the specified text.
+        Note: just like SkCanvas::drawText, this will respect the Align setting
+              in the paint.
+    */
+    void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y,
+                     SkPath* path) const;
+
+#ifdef SK_BUILD_FOR_ANDROID
+    const SkGlyph& getUnicharMetrics(SkUnichar, const SkMatrix*);
+    const SkGlyph& getGlyphMetrics(uint16_t, const SkMatrix*);
+    const void* findImage(const SkGlyph&, const SkMatrix*);
+
+    uint32_t getGenerationID() const;
+
+    /** 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;
+
+private:
+    SkTypeface*     fTypeface;
+    SkScalar        fTextSize;
+    SkScalar        fTextScaleX;
+    SkScalar        fTextSkewX;
+
+    SkPathEffect*   fPathEffect;
+    SkShader*       fShader;
+    SkXfermode*     fXfermode;
+    SkMaskFilter*   fMaskFilter;
+    SkColorFilter*  fColorFilter;
+    SkRasterizer*   fRasterizer;
+    SkDrawLooper*   fLooper;
+    SkImageFilter*  fImageFilter;
+
+    SkColor         fColor;
+    SkScalar        fWidth;
+    SkScalar        fMiterLimit;
+    unsigned        fFlags : 15;
+    unsigned        fTextAlign : 2;
+    unsigned        fCapType : 2;
+    unsigned        fJoinType : 2;
+    unsigned        fStyle : 2;
+    unsigned        fTextEncoding : 2;  // 3 values
+    unsigned        fHinting : 2;
+#ifdef SK_BUILD_FOR_ANDROID
+    SkLanguage      fLanguage;
+    FontVariant     fFontVariant;
+#endif
+
+    SkDrawCacheProc    getDrawCacheProc() const;
+    SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir,
+                                           bool needFullMetrics) const;
+
+    SkScalar measure_text(SkGlyphCache*, const char* text, size_t length,
+                          int* count, SkRect* bounds) const;
+
+    SkGlyphCache*   detachCache(const SkMatrix*) const;
+
+    void descriptorProc(const SkMatrix* deviceMatrix,
+                        void (*proc)(const SkDescriptor*, void*),
+                        void* context, bool ignoreGamma = false) const;
+
+    const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage) const;
+
+    enum {
+        kCanonicalTextSizeForPaths = 64
+    };
+    friend class SkAutoGlyphCache;
+    friend class SkCanvas;
+    friend class SkDraw;
+    friend class SkPDFDevice;
+    friend class SkTextToPathIter;
+
+#ifdef SK_BUILD_FOR_ANDROID
+    // In order for the == operator to work properly this must be the last field
+    // in the struct so that we can do a memcmp to this field's offset.
+    uint32_t        fGenerationID;
+#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/legacy/include/core/SkPath.h b/legacy/include/core/SkPath.h
new file mode 100644
index 0000000..957d50e
--- /dev/null
+++ b/legacy/include/core/SkPath.h
@@ -0,0 +1,784 @@
+
+/*
+ * 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 SkPath_DEFINED
+#define SkPath_DEFINED
+
+#include "SkMatrix.h"
+#include "SkTDArray.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+#define GEN_ID_INC              fGenerationID++
+#define GEN_ID_PTR_INC(ptr)     ptr->fGenerationID++
+#else
+#define GEN_ID_INC
+#define GEN_ID_PTR_INC(ptr)
+#endif
+
+class SkReader32;
+class SkWriter32;
+class SkAutoPathBoundsUpdate;
+class SkString;
+
+/** \class SkPath
+
+    The SkPath class encapsulates compound (multiple contour) geometric paths
+    consisting of straight line segments, quadratic curves, and cubic curves.
+*/
+class SK_API SkPath {
+public:
+    SkPath();
+    SkPath(const SkPath&);
+    ~SkPath();
+
+    SkPath& operator=(const SkPath&);
+
+    friend bool operator==(const SkPath&, const SkPath&);
+    friend bool operator!=(const SkPath& a, const SkPath& b) {
+        return !(a == b);
+    }
+
+    enum FillType {
+        /** Specifies that "inside" is computed by a non-zero sum of signed
+            edge crossings
+        */
+        kWinding_FillType,
+        /** Specifies that "inside" is computed by an odd number of edge
+            crossings
+        */
+        kEvenOdd_FillType,
+        /** Same as Winding, but draws outside of the path, rather than inside
+        */
+        kInverseWinding_FillType,
+        /** Same as EvenOdd, but draws outside of the path, rather than inside
+         */
+        kInverseEvenOdd_FillType
+    };
+
+    /** Return the path's fill type. This is used to define how "inside" is
+        computed. The default value is kWinding_FillType.
+
+        @return the path's fill type
+    */
+    FillType getFillType() const { return (FillType)fFillType; }
+
+    /** Set the path's fill type. This is used to define how "inside" is
+        computed. The default value is kWinding_FillType.
+
+        @param ft The new fill type for this path
+    */
+    void setFillType(FillType ft) {
+        fFillType = SkToU8(ft);
+        GEN_ID_INC;
+    }
+
+    /** Returns true if the filltype is one of the Inverse variants */
+    bool isInverseFillType() const { return (fFillType & 2) != 0; }
+
+    /**
+     *  Toggle between inverse and normal filltypes. This reverse the return
+     *  value of isInverseFillType()
+     */
+    void toggleInverseFillType() {
+        fFillType ^= 2;
+        GEN_ID_INC;
+     }
+
+    enum Convexity {
+        kUnknown_Convexity,
+        kConvex_Convexity,
+        kConcave_Convexity
+    };
+
+    /**
+     *  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.
+     */
+    Convexity getConvexity() const {
+        if (kUnknown_Convexity == fConvexity) {
+            fConvexity = (uint8_t)ComputeConvexity(*this);
+        }
+        return (Convexity)fConvexity;
+    }
+
+    /**
+     *  Return the currently cached value for convexity, even if that is set to
+     *  kUnknown_Convexity. Note: getConvexity() will automatically call
+     *  ComputeConvexity and cache its return value if the current setting is
+     *  kUnknown.
+     */
+    Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; }
+
+    /**
+     *  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().
+     *
+     *  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
+     *  reset to kUnknown_Convexity.
+     */
+    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.
+     */
+    bool isConvex() const {
+        return kConvex_Convexity == this->getConvexity();
+    }
+
+    /**
+     *  DEPRECATED: use setConvexity()
+     *  Set the isConvex flag to true or false. Convex paths may draw faster if
+     *  this flag is set, though setting this to true on a path that is in fact
+     *  not convex can give undefined results when drawn. Paths default to
+     *  isConvex == false
+     */
+    void setIsConvex(bool isConvex) {
+        this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
+    }
+
+    /** 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.
+        This does NOT change the fill-type setting nor isConvex
+    */
+    void rewind();
+
+    /** Returns true if the path is empty (contains no lines or curves)
+
+        @return true if the path is empty (contains no lines or curves)
+    */
+    bool isEmpty() const;
+
+    /** 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);
+    }
+
+    /** Test a quad for zero length
+
+        @return true if the quad is of zero length; otherwise false.
+    */
+    static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
+                                 const SkPoint& p3) {
+        return p1.equalsWithinTolerance(p2, SK_ScalarNearlyZero) &&
+               p2.equalsWithinTolerance(p3, SK_ScalarNearlyZero);
+    }
+
+    /** Test a cubic curve for zero length
+
+        @return true if the cubic is of zero length; otherwise false.
+    */
+    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);
+    }
+
+    /** 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;
+
+    /** Return the number of points in the path
+     */
+    int countPoints() const {
+        return this->getPoints(NULL, 0);
+    }
+
+    /** 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
+         will be (0,0)
+     */
+    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;
+
+    //! Swap contents of this and other. Guaranteed not to throw
+    void swap(SkPath& other);
+
+    /** Returns the bounds of the path's points. If the path contains 0 or 1
+        points, the bounds is set to (0,0,0,0), and isEmpty() will return true.
+        Note: this bounds may be larger than the actual shape, since curves
+        do not extend as far as their control points.
+    */
+    const SkRect& getBounds() const {
+        if (fBoundsIsDirty) {
+            this->computeBounds();
+        }
+        return fBounds;
+    }
+
+    /** 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.
+        This also means that any copies or simple transformations of the path
+        will inherit the cached bounds.
+     */
+    void updateBoundsCache() const {
+        // for now, just calling getBounds() is sufficient
+        this->getBounds();
+    }
+
+    //  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) {
+        this->moveTo(p.fX, p.fY);
+    }
+
+    /** 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
+                    previous contour, to specify the start of a new contour
+    */
+    void rMoveTo(SkScalar dx, SkScalar dy);
+
+    /** Add a line from the last point to the specified point (x,y). If no
+        moveTo() call has been made for this contour, the first point is
+        automatically set to (0,0).
+
+        @param x    The x-coordinate of the end of a line
+        @param y    The y-coordinate of the end of a line
+    */
+    void lineTo(SkScalar x, SkScalar y);
+
+    /** Add a line from the last point to the specified point. If no moveTo()
+        call has been made for this contour, the first point is automatically
+        set to (0,0).
+
+        @param p    The end of a line
+    */
+    void lineTo(const SkPoint& p) {
+        this->lineTo(p.fX, p.fY);
+    }
+
+    /** 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
+                    on this contour, to specify a line
+    */
+    void rLineTo(SkScalar dx, SkScalar dy);
+
+    /** 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
+        @param y2   The y-coordinate of the end point on a quadratic curve
+    */
+    void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
+
+    /** 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
+    */
+    void quadTo(const SkPoint& p1, const SkPoint& p2) {
+        this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
+    }
+
+    /** Same as quadTo, 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 dx1   The amount to add to the x-coordinate of the last point on
+                this contour, to specify the control point of a quadratic curve
+        @param dy1   The amount to add to the y-coordinate of the last point on
+                this contour, to specify the control point of a quadratic curve
+        @param dx2   The amount to add to the x-coordinate of the last point on
+                     this contour, to specify the end point of a quadratic curve
+        @param dy2   The amount to add to the y-coordinate of the last point on
+                     this contour, to specify the end point of a quadratic curve
+    */
+    void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
+
+    /** 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
+        @param y2   The y-coordinate of the 2nd control point on a cubic curve
+        @param x3   The x-coordinate of the end point on a cubic curve
+        @param y3   The y-coordinate of the end point on a cubic curve
+    */
+    void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                 SkScalar x3, SkScalar y3);
+
+    /** 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
+    */
+    void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
+        this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
+    }
+
+    /** 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
+                this contour, to specify the 1st control point of a cubic curve
+        @param dx2   The amount to add to the x-coordinate of the last point on
+                this contour, to specify the 2nd control point of a cubic curve
+        @param dy2   The amount to add to the y-coordinate of the last point on
+                this contour, to specify the 2nd control point of a cubic curve
+        @param dx3   The amount to add to the x-coordinate of the last point on
+                     this contour, to specify the end point of a cubic curve
+        @param dy3   The amount to add to the y-coordinate of the last point on
+                     this contour, to specify the end point of a cubic curve
+    */
+    void    rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                     SkScalar x3, SkScalar y3);
+
+    /** Append the specified arc to the path as a new contour. If the start of
+        the path is different from the path's current last point, then an
+        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
+                          treated mod 360.
+        @param forceMoveTo If true, always begin a new contour with the arc
+    */
+    void    arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+                  bool forceMoveTo);
+
+    /** Append a line and arc to the current path. This is the same as the
+        PostScript call "arct".
+    */
+    void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+               SkScalar radius);
+
+    /** Append a line and arc to the current path. This is the same as the
+        PostScript call "arct".
+    */
+    void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
+        this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
+    }
+
+    /** Close the current contour. If the current point is not equal to the
+        first point of the contour, a line segment is automatically added.
+    */
+    void close();
+
+    enum Direction {
+        /** clockwise direction for adding closed contours */
+        kCW_Direction,
+        /** counter-clockwise direction for adding closed contours */
+        kCCW_Direction
+    };
+
+    /**
+     *  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.
+     */
+    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.
+     */
+    bool cheapIsDirection(Direction dir) const {
+        Direction computedDir;
+        return this->cheapComputeDirection(&computedDir) && 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
+    */
+    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
+    */
+    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
+    */
+    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
+    */
+    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
+    */
+    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
+        */
+    void addRoundRect(const SkRect& rect, const SkScalar radii[],
+                      Direction dir = kCW_Direction);
+
+    /** 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
+        @param dx   The amount to translate the path in Y as it is added
+    */
+    void addPath(const SkPath& src, SkScalar dx, SkScalar dy);
+
+    /** Add a copy of src to the path
+    */
+    void addPath(const SkPath& src) {
+        SkMatrix m;
+        m.reset();
+        this->addPath(src, m);
+    }
+
+    /** Add a copy of src to the path, transformed by matrix
+        @param src  The path to add as a new contour
+    */
+    void addPath(const SkPath& src, const SkMatrix& matrix);
+
+    /**
+     *  Same as addPath(), but reverses the src input
+     */
+    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 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 
+    */
+    void offset(SkScalar dx, SkScalar dy) {
+        this->offset(dx, dy, this);
+    }
+
+    /** 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
+    */
+    void transform(const SkMatrix& matrix, SkPath* dst) const;
+
+    /** Transform the points in this path by matrix
+
+        @param matrix The matrix to apply to the path
+    */
+    void transform(const SkMatrix& matrix) {
+        this->transform(matrix, this);
+    }
+
+    /** 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
+    */
+    void setLastPt(SkScalar x, SkScalar y);
+
+    /** Set the last point on the path. If no points have been added, moveTo(p)
+        is automatically called.
+
+        @param p    The new location for the last point
+    */
+    void setLastPt(const SkPoint& p) {
+        this->setLastPt(p.fX, p.fY);
+    }
+
+    enum SegmentMask {
+        kLine_SegmentMask   = 1 << 0,
+        kQuad_SegmentMask   = 1 << 1,
+        kCubic_SegmentMask  = 1 << 2
+    };
+
+    /**
+     *  Returns a mask, where each bit corresponding to a SegmentMask is
+     *  set if the path contains 1 or more segments of that type.
+     *  Returns 0 for an empty path (no segments).
+     */
+    uint32_t getSegmentMasks() const { return fSegmentMask; }
+
+    enum Verb {
+        kMove_Verb,     //!< iter.next returns 1 point
+        kLine_Verb,     //!< iter.next returns 2 points
+        kQuad_Verb,     //!< iter.next returns 3 points
+        kCubic_Verb,    //!< iter.next returns 4 points
+        kClose_Verb,    //!< iter.next returns 1 point (contour's moveTo pt)
+        kDone_Verb      //!< iter.next returns 0 points
+    };
+
+    /** Iterate through all of the segments (lines, quadratics, cubics) of
+        each contours in a path.
+
+        The iterator cleans up the segments along the way, removing degenerate
+        segments and adding close verbs where necessary. When the forceClose
+        argument is provided, each contour (as defined by a new starting
+        move command) will be completed with a close verb regardless of the
+        contour's contents.
+    */
+    class SK_API Iter {
+    public:
+        Iter();
+        Iter(const SkPath&, bool forceClose);
+
+        void setPath(const SkPath&, bool forceClose);
+
+        /** 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
+            @return The verb for the current segment
+        */
+        Verb next(SkPoint pts[4]);
+
+        /** 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
+            initial moveto for this contour). If next() returned a different
+            verb, this returns an undefined value.
+
+            @return If the last call to next() returned kLine_Verb, return true
+                    if it was the result of an explicit close command.
+        */
+        bool isCloseLine() const { return SkToBool(fCloseLine); }
+
+        /** Returns true if the current contour is closed (has a kClose_Verb)
+            @return true if the current contour is closed (has a kClose_Verb)
+        */
+        bool isClosedContour() const;
+
+    private:
+        const SkPoint*  fPts;
+        const uint8_t*  fVerbs;
+        const uint8_t*  fVerbStop;
+        SkPoint         fMoveTo;
+        SkPoint         fLastPt;
+        SkBool8         fForceClose;
+        SkBool8         fNeedClose;
+        SkBool8         fCloseLine;
+        SkBool8         fSegmentState;
+
+        bool cons_moveTo(SkPoint pts[1]);
+        Verb autoClose(SkPoint pts[2]);
+        void consumeDegenerateSegments();
+    };
+
+    /** Iterate through the verbs in the path, providing the associated points.
+    */
+    class SK_API RawIter {
+    public:
+        RawIter();
+        RawIter(const SkPath&);
+
+        void setPath(const SkPath&);
+
+        /** 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
+            @return The verb for the current segment
+        */
+        Verb next(SkPoint pts[4]);
+
+    private:
+        const SkPoint*  fPts;
+        const uint8_t*  fVerbs;
+        const uint8_t*  fVerbStop;
+        SkPoint         fMoveTo;
+        SkPoint         fLastPt;
+    };
+
+    void dump(bool forceClose, const char title[] = NULL) const;
+    void dump() const;
+
+    void flatten(SkWriter32&) const;
+    void unflatten(SkReader32&);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    uint32_t getGenerationID() const;
+    const SkPath* getSourcePath() const;
+    void setSourcePath(const SkPath* path);
+#endif
+
+    SkDEBUGCODE(void validate() const;)
+
+private:
+    SkTDArray<SkPoint>  fPts;
+    SkTDArray<uint8_t>  fVerbs;
+    mutable SkRect      fBounds;
+    int                 fLastMoveToIndex;
+    uint8_t             fFillType;
+    uint8_t             fSegmentMask;
+    mutable uint8_t     fBoundsIsDirty;
+    mutable uint8_t     fConvexity;
+#ifdef SK_BUILD_FOR_ANDROID
+    uint32_t            fGenerationID;
+    const SkPath*       fSourcePath;
+#endif
+
+    // called, if dirty, by getBounds()
+    void computeBounds() const;
+
+    friend class Iter;
+
+    friend class SkPathStroker;
+    /*  Append the first contour of path, ignoring path's initial point. If no
+        moveTo() call has been made for this contour, the first point is
+        automatically set to (0,0).
+    */
+    void pathTo(const SkPath& path);
+
+    /*  Append, in reverse order, the first contour of path, ignoring path's
+        last point. If no moveTo() call has been made for this contour, the
+        first point is automatically set to (0,0).
+    */
+    void reversePathTo(const SkPath&);
+
+    // called before we add points for lineTo, quadTo, cubicTo, checking to see
+    // if we need to inject a leading moveTo first
+    //
+    //  SkPath path; path.lineTo(...);   <--- need a leading moveTo(0, 0)
+    // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
+    //
+    inline void injectMoveToIfNeeded();
+
+    friend class SkAutoPathBoundsUpdate;
+};
+
+#endif
diff --git a/legacy/include/core/SkPathEffect.h b/legacy/include/core/SkPathEffect.h
new file mode 100644
index 0000000..1b4cd5f
--- /dev/null
+++ b/legacy/include/core/SkPathEffect.h
@@ -0,0 +1,137 @@
+
+/*
+ * 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 SkPathEffect_DEFINED
+#define SkPathEffect_DEFINED
+
+#include "SkFlattenable.h"
+
+class SkPath;
+
+/** \class SkPathEffect
+
+    SkPathEffect is the base class for objects in the SkPaint that affect
+    the geometry of a drawing primitive before it is transformed by the
+    canvas' matrix and drawn.
+
+    Dashing is implemented as a subclass of SkPathEffect.
+*/
+class SK_API SkPathEffect : public SkFlattenable {
+public:
+    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;
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+private:
+    // illegal
+    SkPathEffect(const SkPathEffect&);
+    SkPathEffect& operator=(const SkPathEffect&);
+};
+
+/** \class SkPairPathEffect
+
+    Common baseclass for Compose and Sum. This subclass manages two pathEffects,
+    including flattening them. It does nothing in filterPath, and is only useful
+    for managing the lifetimes of its two arguments.
+*/
+class SkPairPathEffect : public SkPathEffect {
+public:
+    SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1);
+    virtual ~SkPairPathEffect();
+
+protected:
+    SkPairPathEffect(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    // these are visible to our subclasses
+    SkPathEffect* fPE0, *fPE1;
+    
+private:
+    typedef SkPathEffect INHERITED;
+};
+
+/** \class SkComposePathEffect
+
+    This subclass of SkPathEffect composes its two arguments, to create
+    a compound pathEffect.
+*/
+class SkComposePathEffect : public SkPairPathEffect {
+public:
+    /** Construct a pathEffect whose effect is to apply first the inner pathEffect
+        and the the outer pathEffect (e.g. outer(inner(path)))
+        The reference counts for outer and inner are both incremented in the constructor,
+        and decremented in the destructor.
+    */
+    SkComposePathEffect(SkPathEffect* outer, SkPathEffect* inner)
+        : INHERITED(outer, inner) {}
+
+    // overrides
+    
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkComposePathEffect, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+
+private:
+    SkComposePathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    // illegal
+    SkComposePathEffect(const SkComposePathEffect&);
+    SkComposePathEffect& operator=(const SkComposePathEffect&);
+    
+    typedef SkPairPathEffect INHERITED;
+};
+
+/** \class SkSumPathEffect
+
+    This subclass of SkPathEffect applies two pathEffects, one after the other.
+    Its filterPath() returns true if either of the effects succeeded.
+*/
+class SkSumPathEffect : public SkPairPathEffect {
+public:
+    /** Construct a pathEffect whose effect is to apply two effects, in sequence.
+        (e.g. first(path) + second(path))
+        The reference counts for first and second are both incremented in the constructor,
+        and decremented in the destructor.
+    */
+    SkSumPathEffect(SkPathEffect* first, SkPathEffect* second)
+        : INHERITED(first, second) {}
+
+    // overrides
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)  {
+        return SkNEW_ARGS(SkSumPathEffect, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+
+private:
+    SkSumPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    // illegal
+    SkSumPathEffect(const SkSumPathEffect&);
+    SkSumPathEffect& operator=(const SkSumPathEffect&);
+
+    typedef SkPairPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkPathMeasure.h b/legacy/include/core/SkPathMeasure.h
new file mode 100644
index 0000000..6fb4482
--- /dev/null
+++ b/legacy/include/core/SkPathMeasure.h
@@ -0,0 +1,107 @@
+
+/*
+ * 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 SkPathMeasure_DEFINED
+#define SkPathMeasure_DEFINED
+
+#include "SkPath.h"
+#include "SkTDArray.h"
+
+class SkPathMeasure : SkNoncopyable {
+public:
+    SkPathMeasure();
+    /** Initialize the pathmeasure with the specified path. The path must remain valid
+        for the lifetime of the measure object, or until setPath() is called with
+        a different path (or null), since the measure object keeps a pointer to the
+        path object (does not copy its data).
+    */
+    SkPathMeasure(const SkPath& path, bool forceClosed);
+    ~SkPathMeasure();
+
+    /** Reset the pathmeasure with the specified path. The path must remain valid
+        for the lifetime of the measure object, or until setPath() is called with
+        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);
+
+    /** Return the total length of the current contour, or 0 if no path
+        is associated (e.g. resetPath(null))
+    */
+    SkScalar getLength();
+
+    /** Pins distance to 0 <= distance <= getLength(), and then computes
+        the corresponding position and tangent.
+        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);
+
+    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);
+    /** 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
+        then return false (and leave dst untouched).
+        Begin the segment with a moveTo if startWithMoveTo is true
+    */
+    bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo);
+
+    /** Return true if the current contour is closed()
+    */
+    bool isClosed();
+
+    /** Move to the next contour in the path. Return true if one exists, or false if
+        we're done with the path.
+    */
+    bool nextContour();
+
+#ifdef SK_DEBUG
+    void    dump();
+#endif
+
+private:
+    SkPath::Iter    fIter;
+    const SkPath*   fPath;
+    SkScalar        fLength;            // relative to the current contour
+    int             fFirstPtIndex;      // relative to the current contour
+    bool            fIsClosed;          // relative to the current contour
+    bool            fForceClosed;
+
+    struct Segment {
+        SkScalar    fDistance;  // total distance up to this point
+        unsigned    fPtIndex : 15; // index into the fPts array
+        unsigned    fTValue : 15;
+        unsigned    fType : 2;
+
+        SkScalar getScalarT() const;
+    };
+    SkTDArray<Segment>  fSegments;
+    SkTDArray<SkPoint>  fPts; // Points used to define the segments
+
+    static const Segment* NextSegment(const Segment*);
+
+    void     buildSegments();
+    SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance,
+                                int mint, int maxt, int ptIndex);
+    SkScalar compute_cubic_segs(const SkPoint pts[3], SkScalar distance,
+                                int mint, int maxt, int ptIndex);
+    const Segment* distanceToSegment(SkScalar distance, SkScalar* t);
+};
+
+#endif
diff --git a/include/core/SkPerspIter.h b/legacy/include/core/SkPerspIter.h
similarity index 100%
rename from include/core/SkPerspIter.h
rename to legacy/include/core/SkPerspIter.h
diff --git a/legacy/include/core/SkPicture.h b/legacy/include/core/SkPicture.h
new file mode 100644
index 0000000..47a2b95
--- /dev/null
+++ b/legacy/include/core/SkPicture.h
@@ -0,0 +1,136 @@
+
+/*
+ * Copyright 2007 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 SkPicture_DEFINED
+#define SkPicture_DEFINED
+
+#include "SkRefCnt.h"
+
+class SkCanvas;
+class SkPicturePlayback;
+class SkPictureRecord;
+class SkStream;
+class SkWStream;
+
+/** \class SkPicture
+
+    The SkPicture class records the drawing commands made to a canvas, to
+    be played back at a later time.
+*/
+class SK_API SkPicture : public SkRefCnt {
+public:
+    /** 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.
+    */
+    SkPicture();
+    /** Make a copy of the contents of src. If src records more drawing after
+        this call, those elements will not appear in this picture.
+    */
+    SkPicture(const SkPicture& src);
+    explicit SkPicture(SkStream*);
+    virtual ~SkPicture();
+    
+    /**
+     *  Swap the contents of the two pictures. Guaranteed to succeed.
+     */
+    void swap(SkPicture& other);
+    
+    enum RecordingFlags {
+        /*  This flag specifies that when clipPath() is called, the path will
+            be faithfully recorded, but the recording canvas' current clip will
+            only see the path's bounds. This speeds up the recording process
+            without compromising the fidelity of the playback. The only side-
+            effect for recording is that calling getTotalClip() or related
+            clip-query calls will reflect the path's bounds, not the actual
+            path.
+         */
+        kUsePathBoundsForClip_RecordingFlag = 0x01
+    };
+
+    /** Returns the canvas that records the drawing commands.
+        @param width the base width for the picture, as if the recording
+                     canvas' bitmap had this width.
+        @param height the base width for the picture, as if the recording
+                     canvas' bitmap had this height.
+        @param recordFlags optional flags that control recording.
+        @return the picture canvas.
+    */
+    SkCanvas* beginRecording(int width, int height, uint32_t recordFlags = 0);
+
+    /** Returns the recording canvas if one is active, or NULL if recording is
+        not active. This does not alter the refcnt on the canvas (if present).
+    */
+    SkCanvas* getRecordingCanvas() const;
+    /** Signal that the caller is done recording. This invalidates the canvas
+        returned by beginRecording/getRecordingCanvas, and prepares the picture
+        for drawing. Note: this happens implicitly the first time the picture
+        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.
+        @return the width of the picture's recording canvas
+    */
+    int width() const { return fWidth; }
+
+    /** Return the height 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.
+        @return the height of the picture's recording canvas
+    */
+    int height() const { return fHeight; }
+
+    void serialize(SkWStream*) const;
+
+    /** 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. 
+    */
+    void abortPlayback();
+    
+private:
+    int fWidth, fHeight;
+    SkPictureRecord* fRecord;
+    SkPicturePlayback* fPlayback;
+
+    friend class SkFlatPicture;
+    friend class SkPicturePlayback;
+};
+
+class SkAutoPictureRecord : SkNoncopyable {
+public:
+    SkAutoPictureRecord(SkPicture* pict, int width, int height,
+                        uint32_t recordingFlags = 0) {
+        fPicture = pict;
+        fCanvas = pict->beginRecording(width, height, recordingFlags);
+    }
+    ~SkAutoPictureRecord() {
+        fPicture->endRecording();
+    }
+    
+    /** Return the canvas to draw into for recording into the picture.
+    */
+    SkCanvas* getRecordingCanvas() const { return fCanvas; }
+    
+private:
+    SkPicture*  fPicture;
+    SkCanvas*   fCanvas;
+};
+
+
+#endif
diff --git a/legacy/include/core/SkPixelRef.h b/legacy/include/core/SkPixelRef.h
new file mode 100644
index 0000000..f01ba15
--- /dev/null
+++ b/legacy/include/core/SkPixelRef.h
@@ -0,0 +1,245 @@
+
+/*
+ * 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 SkPixelRef_DEFINED
+#define SkPixelRef_DEFINED
+
+#include "SkBitmap.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+class SkColorTable;
+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
+    SkBitmap. A pixelref is installed into a bitmap, and then the bitmap can
+    access the actual pixel memory by calling lockPixels/unlockPixels.
+
+    This class can be shared/accessed between multiple threads.
+*/
+class SK_API SkPixelRef : public SkRefCnt {
+public:
+    explicit SkPixelRef(SkBaseMutex* mutex = NULL);
+
+    /** Return the pixel memory returned from lockPixels, or null if the
+        lockCount is 0.
+    */
+    void* pixels() const { return fPixels; }
+
+    /** Return the current colorTable (if any) if pixels are locked, or null.
+    */
+    SkColorTable* colorTable() const { return fColorTable; }
+
+    /**
+     *  Returns true if the lockcount > 0
+     */
+    bool isLocked() const { return fLockCount > 0; }
+
+    /** Call to access the pixel memory, which is returned. Balance with a call
+        to unlockPixels().
+    */
+    void lockPixels();
+    /** Call to balanace a previous call to lockPixels(). Returns the pixels
+        (or null) after the unlock. NOTE: lock calls can be nested, but the
+        matching number of unlock calls must be made in order to free the
+        memory (if the subclass implements caching/deferred-decoding.)
+    */
+    void unlockPixels();
+
+    /**
+     *  Some bitmaps can return a copy of their pixels for lockPixels(), but
+     *  that copy, if modified, will not be pushed back. These bitmaps should
+     *  not be used as targets for a raster device/canvas (since all pixels
+     *  modifications will be lost when unlockPixels() is called.)
+     */
+    bool lockPixelsAreWritable() const;
+
+    /** Returns a non-zero, unique value corresponding to the pixels in this
+        pixelref. Each time the pixels are changed (and notifyPixelsChanged is
+        called), a different generation ID will be returned.
+    */
+    uint32_t getGenerationID() const;
+
+    /** Call this if you have changed the contents of the pixels. This will in-
+        turn cause a different generation ID value to be returned from
+        getGenerationID().
+    */
+    void notifyPixelsChanged();
+
+    /** Returns true if this pixelref is marked as immutable, meaning that the
+        contents of its pixels will not change for the lifetime of the pixelref.
+    */
+    bool isImmutable() const { return fIsImmutable; }
+
+    /** Marks this pixelref is immutable, meaning that the contents of its
+        pixels will not change for the lifetime of the pixelref. This state can
+        be set on a pixelref, but it cannot be cleared once it is set.
+    */
+    void setImmutable();
+
+    /** Return the optional URI string associated with this pixelref. May be
+        null.
+    */
+    const char* getURI() const { return fURI.size() ? fURI.c_str() : NULL; }
+
+    /** Copy a URI string to this pixelref, or clear the URI if the uri is null
+     */
+    void setURI(const char uri[]) {
+        fURI.set(uri);
+    }
+
+    /** Copy a URI string to this pixelref
+     */
+    void setURI(const char uri[], size_t len) {
+        fURI.set(uri, len);
+    }
+
+    /** Assign a URI string to this pixelref.
+    */
+    void setURI(const SkString& uri) { fURI = uri; }
+
+    /** 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;
+
+#ifdef SK_BUILD_FOR_ANDROID
+    /**
+     *  Acquire a "global" ref on this object.
+     *  The default implementation just calls ref(), but subclasses can override
+     *  this method to implement additional behavior.
+     */
+    virtual void globalRef(void* data=NULL);
+
+    /**
+     *  Release a "global" ref on this object.
+     *  The default implementation just calls unref(), but subclasses can override
+     *  this method to implement additional behavior.
+     */
+    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.
+    */
+    virtual void* onLockPixels(SkColorTable**) = 0;
+    /** Called when the lock count goes from 1 to 0. The caller will have
+        already acquire a mutex for thread safety, so this method need not do
+        that.
+    */
+    virtual void onUnlockPixels() = 0;
+
+    /** Default impl returns true */
+    virtual bool onLockPixelsAreWritable() const;
+
+    /**
+     *  For pixelrefs that don't have access to their raw pixels, they may be
+     *  able to make a copy of them (e.g. if the pixels are on the GPU).
+     *
+     *  The base class implementation returns false;
+     */
+    virtual bool onReadPixels(SkBitmap* dst, const SkIRect* subsetOrNull);
+
+    /** 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; }
+
+    SkPixelRef(SkFlattenableReadBuffer&, SkBaseMutex*);
+
+    // 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;
+    SkColorTable*   fColorTable;    // we do not track ownership, subclass does
+    int             fLockCount;
+
+    mutable uint32_t fGenerationID;
+
+    SkString    fURI;
+
+    // can go from false to true, but never from true to false
+    bool    fIsImmutable;
+    // only ever set in constructor, const after that
+    bool    fPreLocked;
+
+    void setMutex(SkBaseMutex* mutex);
+
+    friend class SkGraphics;
+};
+
+#endif
diff --git a/legacy/include/core/SkPoint.h b/legacy/include/core/SkPoint.h
new file mode 100644
index 0000000..d371e64
--- /dev/null
+++ b/legacy/include/core/SkPoint.h
@@ -0,0 +1,458 @@
+
+/*
+ * 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 SkPoint_DEFINED
+#define SkPoint_DEFINED
+
+#include "SkMath.h"
+#include "SkScalar.h"
+
+/** \struct SkIPoint
+
+    SkIPoint holds two 32 bit integer coordinates
+*/
+struct SkIPoint {
+    int32_t fX, fY;
+
+    static SkIPoint Make(int32_t x, int32_t y) {
+        SkIPoint pt;
+        pt.set(x, y);
+        return pt;
+    }
+
+    int32_t x() const { return fX; }
+    int32_t y() const { return fY; }
+    void setX(int32_t x) { fX = x; }
+    void setY(int32_t y) { fY = y; }
+
+    /**
+     *  Returns true iff fX and fY are both zero.
+     */
+    bool isZero() const { return (fX | fY) == 0; }
+
+    /**
+     *  Set both fX and fY to zero. Same as set(0, 0)
+     */
+    void setZero() { fX = fY = 0; }
+
+    /** Set the x and y values of the point. */
+    void set(int32_t x, int32_t y) { fX = x; fY = y; }
+
+    /** Rotate the point clockwise, writing the new point into dst
+        It is legal for dst == this
+    */
+    void rotateCW(SkIPoint* dst) const;
+
+    /** Rotate the point clockwise, writing the new point back into the point
+    */
+
+    void rotateCW() { this->rotateCW(this); }
+
+    /** Rotate the point counter-clockwise, writing the new point into dst.
+        It is legal for dst == this
+    */
+    void rotateCCW(SkIPoint* dst) const;
+
+    /** Rotate the point counter-clockwise, writing the new point back into
+        the point
+    */
+    void rotateCCW() { this->rotateCCW(this); }
+
+    /** Negate the X and Y coordinates of the point.
+    */
+    void negate() { fX = -fX; fY = -fY; }
+
+    /** Return a new point whose X and Y coordinates are the negative of the
+        original point's
+    */
+    SkIPoint operator-() const {
+        SkIPoint neg;
+        neg.fX = -fX;
+        neg.fY = -fY;
+        return neg;
+    }
+
+    /** Add v's coordinates to this point's */
+    void operator+=(const SkIPoint& v) {
+        fX += v.fX;
+        fY += v.fY;
+    }
+
+    /** Subtract v's coordinates from this point's */
+    void operator-=(const SkIPoint& v) {
+        fX -= v.fX;
+        fY -= v.fY;
+    }
+
+    /** Returns true if the point's coordinates equal (x,y) */
+    bool equals(int32_t x, int32_t y) const {
+        return fX == x && fY == y;
+    }
+
+    friend bool operator==(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX == b.fX && a.fY == b.fY;
+    }
+
+    friend bool operator!=(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX != b.fX || a.fY != b.fY;
+    }
+
+    /** Returns a new point whose coordinates are the difference between
+        a and b (i.e. a - b)
+    */
+    friend SkIPoint operator-(const SkIPoint& a, const SkIPoint& b) {
+        SkIPoint v;
+        v.set(a.fX - b.fX, a.fY - b.fY);
+        return v;
+    }
+
+    /** Returns a new point whose coordinates are the sum of a and b (a + b)
+    */
+    friend SkIPoint operator+(const SkIPoint& a, const SkIPoint& b) {
+        SkIPoint v;
+        v.set(a.fX + b.fX, a.fY + b.fY);
+        return v;
+    }
+
+    /** Returns the dot product of a and b, treating them as 2D vectors
+    */
+    static int32_t DotProduct(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX * b.fX + a.fY * b.fY;
+    }
+
+    /** Returns the cross product of a and b, treating them as 2D vectors
+    */
+    static int32_t CrossProduct(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX * b.fY - a.fY * b.fX;
+    }
+};
+
+struct SK_API SkPoint {
+    SkScalar    fX, fY;
+
+    static SkPoint Make(SkScalar x, SkScalar y) {
+        SkPoint pt;
+        pt.set(x, y);
+        return pt;
+    }
+
+    SkScalar x() const { return fX; }
+    SkScalar y() const { return fY; }
+
+    /** Set the point's X and Y coordinates */
+    void set(SkScalar x, SkScalar y) { fX = x; fY = y; }
+
+    /** Set the point's X and Y coordinates by automatically promoting (x,y) to
+        SkScalar values.
+    */
+    void iset(int32_t x, int32_t y) {
+        fX = SkIntToScalar(x);
+        fY = SkIntToScalar(y);
+    }
+
+    /** Set the point's X and Y coordinates by automatically promoting p's
+        coordinates to SkScalar values.
+    */
+    void iset(const SkIPoint& p) {
+        fX = SkIntToScalar(p.fX);
+        fY = SkIntToScalar(p.fY);
+    }
+
+    void setAbs(const SkPoint& pt) {
+        fX = SkScalarAbs(pt.fX);
+        fY = SkScalarAbs(pt.fY);
+    }
+    
+    // counter-clockwise fan
+    void setIRectFan(int l, int t, int r, int b) {
+        SkPoint* v = this;
+        v[0].set(SkIntToScalar(l), SkIntToScalar(t));
+        v[1].set(SkIntToScalar(l), SkIntToScalar(b));
+        v[2].set(SkIntToScalar(r), SkIntToScalar(b));
+        v[3].set(SkIntToScalar(r), SkIntToScalar(t));
+    }
+    void setIRectFan(int l, int t, int r, int b, size_t stride);
+
+    // counter-clockwise fan
+    void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
+        SkPoint* v = this;
+        v[0].set(l, t);
+        v[1].set(l, b);
+        v[2].set(r, b);
+        v[3].set(r, t);
+    }
+    void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b, size_t stride);
+
+    static void Offset(SkPoint points[], int count, const SkPoint& offset) {
+        Offset(points, count, offset.fX, offset.fY);
+    }
+
+    static void Offset(SkPoint points[], int count, SkScalar dx, SkScalar dy) {
+        for (int i = 0; i < count; ++i) {
+            points[i].offset(dx, dy);
+        }
+    }
+
+    void offset(SkScalar dx, SkScalar dy) {
+        fX += dx;
+        fY += dy;
+    }
+
+    /** Return the euclidian distance from (0,0) to the point
+    */
+    SkScalar length() const { return SkPoint::Length(fX, fY); }
+    SkScalar distanceToOrigin() const { return this->length(); }
+
+    /**
+     *  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);
+
+    bool canNormalize() const {
+        return CanNormalize(fX, fY);
+    }
+
+    /** Set the point (vector) to be unit-length in the same direction as it
+        already points.  If the point has a degenerate length (i.e. nearly 0)
+        then return false and do nothing; otherwise return true.
+    */
+    bool normalize();
+
+    /** Set the point (vector) to be unit-length in the same direction as the
+        x,y params. If the vector (x,y) has a degenerate length (i.e. nearly 0)
+        then return false and do nothing, otherwise return true.
+    */
+    bool setNormalize(SkScalar x, SkScalar y);
+
+    /** Scale the point (vector) to have the specified length, and return that
+        length. If the original length is degenerately small (nearly zero),
+        do nothing and return false, otherwise return true.
+    */
+    bool setLength(SkScalar length);
+
+    /** Set the point (vector) to have the specified length in the same
+     direction as (x,y). If the vector (x,y) has a degenerate length
+     (i.e. nearly 0) then return false and do nothing, otherwise return true.
+    */
+    bool setLength(SkScalar x, SkScalar y, SkScalar length);
+
+    /** Scale the point's coordinates by scale, writing the answer into dst.
+        It is legal for dst == this.
+    */
+    void scale(SkScalar scale, SkPoint* dst) const;
+
+    /** Scale the point's coordinates by scale, writing the answer back into
+        the point.
+    */
+    void scale(SkScalar value) { this->scale(value, this); }
+
+    /** Rotate the point clockwise by 90 degrees, writing the answer into dst.
+        It is legal for dst == this.
+    */
+    void rotateCW(SkPoint* dst) const;
+
+    /** Rotate the point clockwise by 90 degrees, writing the answer back into
+        the point.
+    */
+    void rotateCW() { this->rotateCW(this); }
+
+    /** Rotate the point counter-clockwise by 90 degrees, writing the answer
+        into dst. It is legal for dst == this.
+    */
+    void rotateCCW(SkPoint* dst) const;
+
+    /** Rotate the point counter-clockwise by 90 degrees, writing the answer
+        back into the point.
+    */
+    void rotateCCW() { this->rotateCCW(this); }
+
+    /** Negate the point's coordinates
+    */
+    void negate() {
+        fX = -fX;
+        fY = -fY;
+    }
+
+    /** Returns a new point whose coordinates are the negative of the point's
+    */
+    SkPoint operator-() const {
+        SkPoint neg;
+        neg.fX = -fX;
+        neg.fY = -fY;
+        return neg;
+    }
+
+    /** Add v's coordinates to the point's
+    */
+    void operator+=(const SkPoint& v) {
+        fX += v.fX;
+        fY += v.fY;
+    }
+
+    /** Subtract v's coordinates from the point's
+    */
+    void operator-=(const SkPoint& v) {
+        fX -= v.fX;
+        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; }
+
+    friend bool operator==(const SkPoint& a, const SkPoint& b) {
+        return a.fX == b.fX && a.fY == b.fY;
+    }
+
+    friend bool operator!=(const SkPoint& a, const SkPoint& b) {
+        return a.fX != b.fX || a.fY != b.fY;
+    }
+
+    /** Return true if this and the given point are componentwise within tol.
+    */
+    bool equalsWithinTolerance(const SkPoint& v, SkScalar tol) const {
+        return SkScalarNearlyZero(fX - v.fX, tol)
+               && SkScalarNearlyZero(fY - v.fY, tol);
+    }
+
+    /** Returns a new point whose coordinates are the difference between
+        a's and b's (a - b)
+    */
+    friend SkPoint operator-(const SkPoint& a, const SkPoint& b) {
+        SkPoint v;
+        v.set(a.fX - b.fX, a.fY - b.fY);
+        return v;
+    }
+
+    /** Returns a new point whose coordinates are the sum of a's and b's (a + b)
+    */
+    friend SkPoint operator+(const SkPoint& a, const SkPoint& b) {
+        SkPoint v;
+        v.set(a.fX + b.fX, a.fY + b.fY);
+        return v;
+    }
+
+    /** Returns the euclidian distance from (0,0) to (x,y)
+    */
+    static SkScalar Length(SkScalar x, SkScalar y);
+
+    /** Normalize pt, returning its previous length. If the prev length is too
+        small (degenerate), return 0 and leave pt unchanged. This uses the same
+        tolerance as CanNormalize.
+
+        Note that this method may be significantly more expensive than
+        the non-static normalize(), because it has to return the previous length
+        of the point.  If you don't need the previous length, call the
+        non-static normalize() method instead.
+     */
+    static SkScalar Normalize(SkPoint* pt);
+
+    /** Returns the euclidian distance between a and b
+    */
+    static SkScalar Distance(const SkPoint& a, const SkPoint& b) {
+        return Length(a.fX - b.fX, a.fY - b.fY);
+    }
+
+    /** Returns the dot product of a and b, treating them as 2D vectors
+    */
+    static SkScalar DotProduct(const SkPoint& a, const SkPoint& b) {
+        return SkScalarMul(a.fX, b.fX) + SkScalarMul(a.fY, b.fY);
+    }
+
+    /** Returns the cross product of a and b, treating them as 2D vectors
+    */
+    static SkScalar CrossProduct(const SkPoint& a, const SkPoint& b) {
+        return SkScalarMul(a.fX, b.fY) - SkScalarMul(a.fY, b.fX);
+    }
+
+    SkScalar cross(const SkPoint& vec) const {
+        return CrossProduct(*this, vec);
+    }
+
+    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;
+        return SkScalarMul(dx, dx) + SkScalarMul(dy, dy);
+    }
+
+    /**
+     * The side of a point relative to a line. If the line is from a to b then
+     * the values are consistent with the sign of (b-a) cross (pt-a)
+     */
+    enum Side {
+        kLeft_Side  = -1,
+        kOn_Side    =  0,
+        kRight_Side =  1
+    };
+
+    /**
+     * Returns the squared distance to the infinite line between two pts. Also
+     * optionally returns the side of the line that the pt falls on (looking
+     * along line from a to b)
+     */
+    SkScalar distanceToLineBetweenSqd(const SkPoint& a,
+                                      const SkPoint& b,
+                                      Side* side = NULL) const;
+
+    /**
+     * Returns the distance to the infinite line between two pts. Also
+     * optionally returns the side of the line that the pt falls on (looking
+     * along the line from a to b)
+     */
+    SkScalar distanceToLineBetween(const SkPoint& a,
+                                   const SkPoint& b,
+                                   Side* side = NULL) const {
+        return SkScalarSqrt(this->distanceToLineBetweenSqd(a, b, side));
+    }
+
+    /**
+     * Returns the squared distance to the line segment between pts a and b
+     */
+    SkScalar distanceToLineSegmentBetweenSqd(const SkPoint& a,
+                                             const SkPoint& b) const;
+
+    /**
+     * Returns the distance to the line segment between pts a and b.
+     */
+    SkScalar distanceToLineSegmentBetween(const SkPoint& a,
+                                          const SkPoint& b) const {
+        return SkScalarSqrt(this->distanceToLineSegmentBetweenSqd(a, b));
+    }
+
+    /**
+     * Make this vector be orthogonal to vec. Looking down vec the
+     * new vector will point in direction indicated by side (which
+     * must be kLeft_Side or kRight_Side).
+     */
+    void setOrthog(const SkPoint& vec, Side side = kLeft_Side) {
+        // vec could be this
+        SkScalar tmp = vec.fX;
+        if (kRight_Side == side) {
+            fX = -vec.fY;
+            fY = tmp;
+        } else {
+            SkASSERT(kLeft_Side == side);
+            fX = vec.fY;
+            fY = -tmp;
+        }
+    }
+};
+
+typedef SkPoint SkVector;
+
+#endif
diff --git a/legacy/include/core/SkPostConfig.h b/legacy/include/core/SkPostConfig.h
new file mode 100644
index 0000000..edce334
--- /dev/null
+++ b/legacy/include/core/SkPostConfig.h
@@ -0,0 +1,298 @@
+
+/*
+ * 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 SkPostConfig_DEFINED
+#define SkPostConfig_DEFINED
+
+#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_WINCE)
+    #define SK_BUILD_FOR_WIN
+#endif
+
+#if defined(SK_DEBUG) && defined(SK_RELEASE)
+    #error "cannot define both SK_DEBUG and SK_RELEASE"
+#elif !defined(SK_DEBUG) && !defined(SK_RELEASE)
+    #error "must define either SK_DEBUG or SK_RELEASE"
+#endif
+
+#if defined SK_SUPPORT_UNITTEST && !defined(SK_DEBUG)
+    #error "can't have unittests without debug"
+#endif
+
+#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
+#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
+#endif
+
+#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN)
+    #error "cannot define both SK_CPU_LENDIAN and SK_CPU_BENDIAN"
+#elif !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN)
+    #error "must define either SK_CPU_LENDIAN or SK_CPU_BENDIAN"
+#endif
+
+// ensure the port has defined all of these, or none of them
+#ifdef SK_A32_SHIFT
+    #if !defined(SK_R32_SHIFT) || !defined(SK_G32_SHIFT) || !defined(SK_B32_SHIFT)
+        #error "all or none of the 32bit SHIFT amounts must be defined"
+    #endif
+#else
+    #if defined(SK_R32_SHIFT) || defined(SK_G32_SHIFT) || defined(SK_B32_SHIFT)
+        #error "all or none of the 32bit SHIFT amounts must be defined"
+    #endif
+#endif
+
+#if !defined(SK_HAS_COMPILER_FEATURE)
+    #if defined(__has_feature)
+        #define SK_HAS_COMPILER_FEATURE(x) __has_feature(x)
+    #else
+        #define SK_HAS_COMPILER_FEATURE(x) 0
+    #endif
+#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
+ * some combination of parameters lead to a function call that does not return.
+ * It can then make appropriate assumptions about the parameters in code
+ * executed only if the non-returning function was *not* called.
+ */
+#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() {}
+        }
+    #else
+        #define SkNO_RETURN_HINT() do {} while (false)
+    #endif
+#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
+#endif
+
+#ifndef SK_CRASH
+#if 1   // set to 0 for infinite loop, which can help connecting gdb
+    #define SK_CRASH() do { SkNO_RETURN_HINT(); *(int *)(uintptr_t)0xbbadbeef = 0; } while (false)
+#else
+    #define SK_CRASH() do { SkNO_RETURN_HINT(); } while (true)
+#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
+        #define SK_SCALAR_SLOW_COMPARES
+    #endif
+    #ifndef SK_USE_FLOATBITS
+        #define SK_USE_FLOATBITS
+    #endif
+#endif
+
+#ifdef SK_BUILD_FOR_WIN
+    // we want lean_and_mean when we include windows.h
+    #ifndef WIN32_LEAN_AND_MEAN
+        #define WIN32_LEAN_AND_MEAN
+        #define WIN32_IS_MEAN_WAS_LOCALLY_DEFINED
+    #endif
+
+    #include <windows.h>
+
+    #ifdef WIN32_IS_MEAN_WAS_LOCALLY_DEFINED
+        #undef WIN32_LEAN_AND_MEAN
+    #endif
+
+    #ifndef SK_DEBUGBREAK
+        #define SK_DEBUGBREAK(cond)     do { if (!(cond)) { SkNO_RETURN_HINT(); __debugbreak(); }} while (false)
+    #endif
+
+    #ifndef SK_A32_SHIFT
+        #define SK_A32_SHIFT 24
+        #define SK_R32_SHIFT 16
+        #define SK_G32_SHIFT 8
+        #define SK_B32_SHIFT 0
+    #endif
+
+#elif defined(SK_BUILD_FOR_MAC)
+    #ifndef SK_DEBUGBREAK
+        #define SK_DEBUGBREAK(cond)     do { if (!(cond)) SK_CRASH(); } while (false)
+    #endif
+#else
+    #ifdef SK_DEBUG
+        #include <stdio.h>
+        #ifndef SK_DEBUGBREAK
+            #define SK_DEBUGBREAK(cond) do { if (cond) break; \
+                SkDebugf("%s:%d: failed assertion \"%s\"\n", \
+                __FILE__, __LINE__, #cond); SK_CRASH(); } while (false)
+        #endif
+    #endif
+#endif
+
+/*
+ *  We check to see if the SHIFT value has already been defined.
+ *  if not, we define it ourself to some default values. We default to OpenGL
+ *  order (in memory: r,g,b,a)
+ */
+#ifndef SK_A32_SHIFT
+    #ifdef SK_CPU_BENDIAN
+        #define SK_R32_SHIFT    24
+        #define SK_G32_SHIFT    16
+        #define SK_B32_SHIFT    8
+        #define SK_A32_SHIFT    0
+    #else
+        #define SK_R32_SHIFT    0
+        #define SK_G32_SHIFT    8
+        #define SK_B32_SHIFT    16
+        #define SK_A32_SHIFT    24
+    #endif
+#endif
+
+//  stdlib macros
+
+#if 0
+#if !defined(strlen) && defined(SK_DEBUG)
+    extern size_t sk_strlen(const char*);
+    #define strlen(s)   sk_strlen(s)
+#endif
+#ifndef sk_strcpy
+    #define sk_strcpy(dst, src)     strcpy(dst, src)
+#endif
+#ifndef sk_strchr
+    #define sk_strchr(s, c)         strchr(s, c)
+#endif
+#ifndef sk_strrchr
+    #define sk_strrchr(s, c)        strrchr(s, c)
+#endif
+#ifndef sk_strcmp
+    #define sk_strcmp(s, t)         strcmp(s, t)
+#endif
+#ifndef sk_strncmp
+    #define sk_strncmp(s, t, n)     strncmp(s, t, n)
+#endif
+#ifndef sk_memcpy
+    #define sk_memcpy(dst, src, n)  memcpy(dst, src, n)
+#endif
+#ifndef memmove
+    #define memmove(dst, src, n)    memmove(dst, src, n)
+#endif
+#ifndef sk_memset
+    #define sk_memset(dst, val, n)  memset(dst, val, n)
+#endif
+#ifndef sk_memcmp
+    #define sk_memcmp(s, t, n)      memcmp(s, t, n)
+#endif
+
+#define sk_strequal(s, t)           (!sk_strcmp(s, t))
+#define sk_strnequal(s, t, n)       (!sk_strncmp(s, t, n))
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC)
+    #ifndef SkLONGLONG
+        #ifdef SK_BUILD_FOR_WIN32
+            #define SkLONGLONG  __int64
+        #else
+            #define SkLONGLONG  long long
+        #endif
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+#ifndef SK_BUILD_FOR_WINCE
+#include <string.h>
+#include <stdlib.h>
+#else
+#define _CMNINTRIN_DECLARE_ONLY
+#include "cmnintrin.h"
+#endif
+
+#if defined SK_DEBUG && defined SK_BUILD_FOR_WIN32
+//#define _CRTDBG_MAP_ALLOC
+#ifdef free
+#undef free
+#endif
+#include <crtdbg.h>
+#undef free
+
+#ifdef SK_DEBUGx
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(__cplusplus)
+    void * operator new(
+        size_t cb,
+        int nBlockUse,
+        const char * szFileName,
+        int nLine,
+        int foo
+        );
+    void * operator new[](
+        size_t cb,
+        int nBlockUse,
+        const char * szFileName,
+        int nLine,
+        int foo
+        );
+    void operator delete(
+        void *pUserData,
+        int, const char*, int, int
+        );
+    void operator delete(
+        void *pUserData
+        );
+    void operator delete[]( void * p );
+    #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__, 0)
+#else
+    #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
+#endif
+    #define new DEBUG_CLIENTBLOCK
+#else
+#define DEBUG_CLIENTBLOCK
+#endif // _DEBUG
+
+
+#endif
+
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#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
+#else
+// Linux GCC ignores "__attribute__((override))" and rejects "override".
+#define SK_OVERRIDE
+#endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#ifndef SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 1
+#endif
diff --git a/legacy/include/core/SkPreConfig.h b/legacy/include/core/SkPreConfig.h
new file mode 100644
index 0000000..4485f1d
--- /dev/null
+++ b/legacy/include/core/SkPreConfig.h
@@ -0,0 +1,125 @@
+
+/*
+ * 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 SkPreConfig_DEFINED
+#define SkPreConfig_DEFINED
+
+#ifdef WEBKIT_VERSION_MIN_REQUIRED
+    #include "config.h"
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#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)
+
+    #ifdef __APPLE__
+        #include "TargetConditionals.h"
+    #endif
+
+    #if defined(PALMOS_SDK_VERSION)
+        #define SK_BUILD_FOR_PALM
+    #elif defined(UNDER_CE)
+        #define SK_BUILD_FOR_WINCE
+    #elif defined(WIN32)
+        #define SK_BUILD_FOR_WIN32
+    #elif defined(__SYMBIAN32__)
+        #define SK_BUILD_FOR_WIN32
+    #elif defined(ANDROID_NDK)
+        #define SK_BUILD_FOR_ANDROID_NDK
+    #elif defined(ANDROID)
+        #define SK_BUILD_FOR_ANDROID
+    #elif defined(linux) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
+          defined(__sun) || defined(__NetBSD__) || defined(__DragonFly__)
+        #define SK_BUILD_FOR_UNIX
+    #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+        #define SK_BUILD_FOR_IOS
+    #else
+        #define SK_BUILD_FOR_MAC
+    #endif
+
+#endif
+
+/* 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 
+ * 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
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_DEBUG) && !defined(SK_RELEASE)
+    #ifdef NDEBUG
+        #define SK_RELEASE
+    #else
+        #define SK_DEBUG
+    #endif
+#endif
+
+#ifdef SK_BUILD_FOR_WIN32
+    #if !defined(SK_RESTRICT)
+        #define SK_RESTRICT __restrict
+    #endif
+    #include "sk_stdint.h"
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_RESTRICT)
+    #define SK_RESTRICT __restrict__
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_SCALAR_IS_FLOAT) && !defined(SK_SCALAR_IS_FIXED)
+    #define SK_SCALAR_IS_FLOAT
+    #define SK_CAN_USE_FLOAT
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_CPU_BENDIAN) && !defined(SK_CPU_LENDIAN)
+    #if defined (__ppc__) || defined(__ppc64__)
+        #define SK_CPU_BENDIAN
+    #else
+        #define SK_CPU_LENDIAN
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#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 */
+    #define SK_CPU_HAS_CONDITIONAL_INSTR
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SKIA_IMPLEMENTATION)
+    #define SKIA_IMPLEMENTATION 0
+#endif
+
+#if defined(SKIA_DLL)
+    #if defined(WIN32)
+        #if SKIA_IMPLEMENTATION
+            #define SK_API __declspec(dllexport)
+        #else
+            #define SK_API __declspec(dllimport)
+        #endif
+    #else
+        #define SK_API __attribute__((visibility("default")))
+    #endif
+#else
+    #define SK_API
+#endif
+
+#endif
+
diff --git a/include/core/SkPtrRecorder.h b/legacy/include/core/SkPtrRecorder.h
similarity index 100%
rename from include/core/SkPtrRecorder.h
rename to legacy/include/core/SkPtrRecorder.h
diff --git a/include/core/SkRandom.h b/legacy/include/core/SkRandom.h
similarity index 100%
rename from include/core/SkRandom.h
rename to legacy/include/core/SkRandom.h
diff --git a/legacy/include/core/SkRasterizer.h b/legacy/include/core/SkRasterizer.h
new file mode 100644
index 0000000..d249898
--- /dev/null
+++ b/legacy/include/core/SkRasterizer.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 SkRasterizer_DEFINED
+#define SkRasterizer_DEFINED
+
+#include "SkFlattenable.h"
+#include "SkMask.h"
+
+class SkMaskFilter;
+class SkMatrix;
+class SkPath;
+struct SkIRect;
+
+class SkRasterizer : public SkFlattenable {
+public:
+    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);
+
+    virtual void flatten(SkFlattenableWriteBuffer& ) SK_OVERRIDE {}
+protected:
+    SkRasterizer(SkFlattenableReadBuffer&);
+
+    virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix,
+                             const SkIRect* clipBounds,
+                             SkMask* mask, SkMask::CreateMode mode);
+
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/legacy/include/core/SkReader32.h b/legacy/include/core/SkReader32.h
new file mode 100644
index 0000000..37ace5c
--- /dev/null
+++ b/legacy/include/core/SkReader32.h
@@ -0,0 +1,119 @@
+
+/*
+ * 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 SkReader32_DEFINED
+#define SkReader32_DEFINED
+
+#include "SkScalar.h"
+
+class SkString;
+
+class SkReader32 : SkNoncopyable {
+public:
+    SkReader32() : fCurr(NULL), fStop(NULL), fBase(NULL) {}
+    SkReader32(const void* data, size_t size)  {
+        this->setMemory(data, size);
+    }
+
+    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; }
+    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; }
+    bool isAvailable(uint32_t size) const { return fCurr + size <= fStop; }
+    
+    void rewind() { fCurr = fBase; }
+
+    void setOffset(size_t offset) {
+        SkASSERT(SkAlign4(offset) == offset);
+        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;
+        fCurr += sizeof(value);
+        SkASSERT(fCurr <= fStop);
+        return value;
+    }
+    
+    SkScalar readScalar() {
+        SkASSERT(ptr_align_4(fCurr));
+        SkScalar value = *(const SkScalar*)fCurr;
+        fCurr += sizeof(value);
+        SkASSERT(fCurr <= fStop);
+        return value;
+    }
+    
+    const void* skip(size_t size) {
+        SkASSERT(ptr_align_4(fCurr));
+        const void* addr = fCurr;
+        fCurr += SkAlign4(size);
+        SkASSERT(fCurr <= fStop);
+        return addr;
+    }
+    
+    template <typename T> const T& skipT() {
+        SkASSERT(SkAlign4(sizeof(T)) == sizeof(T));
+        return *(const T*)this->skip(sizeof(T));
+    }
+
+    void read(void* dst, size_t size) {
+        SkASSERT(0 == size || dst != NULL);
+        SkASSERT(ptr_align_4(fCurr));
+        memcpy(dst, fCurr, size);
+        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(); }
+
+    /**
+     *  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
+     *  string within the reader's buffer.
+     */
+    const char* readString(size_t* len = NULL);
+
+    /**
+     *  Read the string (written by SkWriter32::writeString) and return it in
+     *  copy (if copy is not null). Return the length of the string.
+     */
+    size_t readIntoString(SkString* copy);
+
+private:
+    // these are always 4-byte aligned
+    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;
+    }
+#endif
+};
+
+#endif
diff --git a/legacy/include/core/SkRect.h b/legacy/include/core/SkRect.h
new file mode 100644
index 0000000..65e7611
--- /dev/null
+++ b/legacy/include/core/SkRect.h
@@ -0,0 +1,641 @@
+
+/*
+ * 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 SkRect_DEFINED
+#define SkRect_DEFINED
+
+#include "SkPoint.h"
+#include "SkSize.h"
+
+/** \struct SkIRect
+
+    SkIRect holds four 32 bit integer coordinates for a rectangle
+*/
+struct SK_API SkIRect {
+    int32_t fLeft, fTop, fRight, fBottom;
+
+    static SkIRect MakeEmpty() {
+        SkIRect r;
+        r.setEmpty();
+        return r;
+    }
+    
+    static SkIRect MakeWH(int32_t w, int32_t h) {
+        SkIRect r;
+        r.set(0, 0, w, h);
+        return r;
+    }
+    
+    static SkIRect 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) {
+        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) {
+        SkIRect r;
+        r.set(x, y, x + w, y + h);
+        return r;
+    }
+
+    int left() const { return fLeft; }
+    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 */
+    int y() const { return fTop; }
+    /**
+     *  Returns the rectangle's width. This does not check for a valid rect
+     *  (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; }
+    
+    /**
+     *  Return true if the rectangle's width or height are <= 0
+     */
+    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
+
+    friend bool operator==(const SkIRect& a, const SkIRect& b) {
+        return !memcmp(&a, &b, sizeof(a));
+    }
+
+    friend bool operator!=(const SkIRect& a, const SkIRect& b) {
+        return !(a == b);
+    }
+
+    bool is16Bit() const {
+        return  SkIsS16(fLeft) && SkIsS16(fTop) &&
+                SkIsS16(fRight) && SkIsS16(fBottom);
+    }
+
+    /** Set the rectangle to (0,0,0,0)
+    */
+    void setEmpty() { memset(this, 0, sizeof(*this)); }
+
+    void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
+        fLeft   = left;
+        fTop    = top;
+        fRight  = right;
+        fBottom = bottom;
+    }
+    // alias for set(l, t, r, b)
+    void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
+        this->set(left, top, right, bottom);
+    }
+
+    void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
+        fLeft = x;
+        fTop = y;
+        fRight = x + width;
+        fBottom = y + height;
+    }
+
+    /**
+     *  Make the largest representable rectangle
+     */
+    void setLargest() {
+        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).
+     */
+    void setLargestInverted() {
+        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.
+    */
+    void offset(int32_t dx, int32_t dy) {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  += dx;
+        fBottom += dy;
+    }
+
+    void offset(const SkIPoint& delta) {
+        this->offset(delta.fX, delta.fY);
+    }
+
+    /** 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.
+    */
+    void inset(int32_t dx, int32_t dy) {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  -= dx;
+        fBottom -= 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
+        points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
+    */
+    bool contains(int32_t x, int32_t y) const {
+        return  (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
+                (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
+    }
+
+    /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
+        If either rectangle is empty, contains() returns false.
+    */
+    bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
+        return  left < right && top < bottom && !this->isEmpty() && // check for empties
+                fLeft <= left && fTop <= top &&
+                fRight >= right && fBottom >= bottom;
+    }
+
+    /** Returns true if the specified rectangle r is inside or equal to this rectangle.
+    */
+    bool contains(const SkIRect& r) const {
+        return  !r.isEmpty() && !this->isEmpty() &&     // check for empties
+                fLeft <= r.fLeft && fTop <= r.fTop &&
+                fRight >= r.fRight && fBottom >= r.fBottom;
+    }
+
+    /** 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.
+    */
+    bool containsNoEmptyCheck(int32_t left, int32_t top,
+							  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;
+    }
+    
+    /** 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.
+    */
+    bool intersect(const SkIRect& r) {
+        SkASSERT(&r);
+        return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+
+    /** If rectangles a and b intersect, 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.
+    */
+    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) {
+            fLeft   = SkMax32(a.fLeft,   b.fLeft);
+            fTop    = SkMax32(a.fTop,    b.fTop);
+            fRight  = SkMin32(a.fRight,  b.fRight);
+            fBottom = SkMin32(a.fBottom, b.fBottom);
+            return true;
+        }
+        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.
+        If either is, then the return result is undefined. In the debug build,
+        we assert that both rectangles are non-empty.
+    */
+    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);
+            fTop    = SkMax32(a.fTop,    b.fTop);
+            fRight  = SkMin32(a.fRight,  b.fRight);
+            fBottom = SkMin32(a.fBottom, b.fBottom);
+            return true;
+        }
+        return false;
+    }
+
+    /** If the rectangle specified by left,top,right,bottom 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.
+    */
+    bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
+        if (left < right && top < bottom && !this->isEmpty() &&
+                fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
+            if (fLeft < left) fLeft = left;
+            if (fTop < top) fTop = top;
+            if (fRight > right) fRight = right;
+            if (fBottom > bottom) fBottom = bottom;
+            return true;
+        }
+        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.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.
+    */
+    void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
+
+    /** 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.
+    */
+    void join(const SkIRect& r) {
+        this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+
+    /** Swap top/bottom or left/right if there are flipped.
+        This can be called if the edges are computed separately,
+        and may have crossed over each other.
+        When this returns, left <= right && top <= bottom
+    */
+    void sort();
+
+    static const SkIRect& EmptyIRect() {
+        static const SkIRect gEmpty = { 0, 0, 0, 0 };
+        return gEmpty;
+    }
+};
+
+/** \struct SkRect
+*/
+struct SK_API SkRect {
+    SkScalar    fLeft, fTop, fRight, fBottom;
+
+    static SkRect MakeEmpty() {
+        SkRect r;
+        r.setEmpty();
+        return r;
+    }
+
+    static SkRect MakeWH(SkScalar w, SkScalar h) {
+        SkRect r;
+        r.set(0, 0, w, h);
+        return r;
+    }
+
+    static SkRect 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) {
+        SkRect rect;
+        rect.set(l, t, r, b);
+        return rect;
+    }
+
+    static SkRect MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
+        SkRect r;
+        r.set(x, y, x + w, y + h);
+        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
+     *  returns false.
+     */
+    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).
+        // value==value will be true iff value is not NaN
+        return value == value;
+#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 == fLeft)  | (SK_FixedNaN == fTop) |
+                    (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
+        return !isNaN;
+#endif
+    }
+
+    SkScalar    left() const { return fLeft; }
+    SkScalar    top() const { return fTop; }
+    SkScalar    right() const { return fRight; }
+    SkScalar    bottom() const { return fBottom; }
+    SkScalar    width() const { return fRight - fLeft; }
+    SkScalar    height() const { return fBottom - fTop; }
+    SkScalar    centerX() const { return SkScalarHalf(fLeft + fRight); }
+    SkScalar    centerY() const { return SkScalarHalf(fTop + fBottom); }
+
+    friend bool operator==(const SkRect& a, const SkRect& b) {
+        return 0 == memcmp(&a, &b, sizeof(a));
+    }
+
+    friend bool operator!=(const SkRect& a, const SkRect& b) {
+        return 0 != memcmp(&a, &b, sizeof(a));
+    }
+
+    /** return the 4 points that enclose the rectangle
+    */
+    void toQuad(SkPoint quad[4]) const;
+
+    /** Set this rectangle to the empty rectangle (0,0,0,0)
+    */
+    void setEmpty() { memset(this, 0, sizeof(*this)); }
+
+    void set(const SkIRect& src) {
+        fLeft   = SkIntToScalar(src.fLeft);
+        fTop    = SkIntToScalar(src.fTop);
+        fRight  = SkIntToScalar(src.fRight);
+        fBottom = SkIntToScalar(src.fBottom);
+    }
+
+    void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
+        fLeft   = left;
+        fTop    = top;
+        fRight  = right;
+        fBottom = bottom;
+    }
+    // alias for set(l, t, r, b)
+    void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
+        this->set(left, top, right, bottom);
+    }
+
+    /** Initialize the rect with the 4 specified integers. The routine handles
+        converting them to scalars (by calling SkIntToScalar)
+     */
+    void iset(int left, int top, int right, int bottom) {
+        fLeft   = SkIntToScalar(left);
+        fTop    = SkIntToScalar(top);
+        fRight  = SkIntToScalar(right);
+        fBottom = SkIntToScalar(bottom);
+    }
+
+    /** 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);
+
+    // alias for set(pts, count)
+    void setBounds(const SkPoint pts[], int count) {
+        this->set(pts, count);
+    }
+
+    void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
+        fLeft = x;
+        fTop = y;
+        fRight = x + width;
+        fBottom = y + height;
+    }
+
+    /**
+     *  Make the largest representable rectangle
+     */
+    void setLargest() {
+        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).
+     */
+    void setLargestInverted() {
+        fLeft = fTop = SK_ScalarMax;
+        fRight = fBottom = SK_ScalarMin;
+    }
+
+    /** Offset set the rectangle by adding dx to its left and right,
+        and adding dy to its top and bottom.
+    */
+    void offset(SkScalar dx, SkScalar dy) {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  += dx;
+        fBottom += dy;
+    }   
+
+    void offset(const SkPoint& delta) {
+        this->offset(delta.fX, delta.fY);
+    }
+
+    /** 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
+         true for dy and the top and bottom.
+    */
+    void inset(SkScalar dx, SkScalar dy)  {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  -= dx;
+        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 hods
+       true for dy and the top and bottom.
+    */
+    void outset(SkScalar dx, SkScalar dy)  { this->inset(-dx, -dy); }
+
+    /** If this rectangle intersects r, 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.
+    */
+    bool intersect(const SkRect& r);
+
+    /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
+        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.
+    */
+    bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
+
+    /**
+     *  Return true if this rectangle is not empty, and the specified sides of
+     *  a rectangle are not empty, and they intersect.
+     */
+    bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
+        return // first check that both are not empty
+               left < right && top < bottom &&
+               fLeft < fRight && fTop < fBottom &&
+               // now check for intersection
+               fLeft < right && left < fRight &&
+               fTop < bottom && top < fBottom;
+    }
+
+    /** If rectangles a and b intersect, 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.
+     */
+    bool intersect(const SkRect& a, const SkRect& b);
+    
+    /**
+     *  Return true if rectangles a and b are not empty and intersect.
+     */
+    static bool Intersects(const SkRect& a, const SkRect& b) {
+        return  !a.isEmpty() && !b.isEmpty() &&
+                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.
+     */
+    void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
+
+    /** 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.
+    */
+    void join(const SkRect& r) {
+        this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+    // alias for join()
+    void growToInclude(const SkRect& r) { this->join(r); }
+
+    /**
+     *  Grow the rect to include the specified (x,y). After this call, the
+     *  following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
+     *
+     *  This is close, but not quite the same contract as contains(), since
+     *  contains() treats the left and top different from the right and bottom.
+     *  contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
+     *  that contains(x,y) always returns false if the rect is empty.
+     */
+    void growToInclude(SkScalar x, SkScalar y) {
+        fLeft  = SkMinScalar(x, fLeft);
+        fRight = SkMaxScalar(x, fRight);
+        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.
+     *
+     *  Contains treats the left and top differently from the right and bottom.
+     *  The left and top coordinates of the rectangle are themselves considered
+     *  to be inside, while the right and bottom are not. Thus for the rectangle
+     *  {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
+     */
+    bool contains(const SkPoint& p) const {
+        return !this->isEmpty() &&
+               fLeft <= p.fX && p.fX < fRight && fTop <= p.fY && p.fY < fBottom;
+    }
+
+    /**
+     *  Returns true if (x,y) is inside the rectangle, and the rectangle
+     *  is not empty.
+     *
+     *  Contains treats the left and top differently from the right and bottom.
+     *  The left and top coordinates of the rectangle are themselves considered
+     *  to be inside, while the right and bottom are not. Thus for the rectangle
+     *  {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
+     */
+    bool contains(SkScalar x, SkScalar y) const {
+        return  !this->isEmpty() &&
+                fLeft <= x && x < fRight && fTop <= y && y < fBottom;
+    }
+
+    /**
+     *  Return true if this rectangle contains r, and if both rectangles are
+     *  not empty.
+     */
+    bool contains(const SkRect& r) const {
+        return  !r.isEmpty() && !this->isEmpty() &&
+                fLeft <= r.fLeft && fTop <= r.fTop &&
+                fRight >= r.fRight && fBottom >= r.fBottom;
+    }
+
+    /**
+     *  Set the dst rectangle by rounding this rectangle's coordinates to their
+     *  nearest integer values using SkScalarRound.
+     */
+    void round(SkIRect* dst) const {
+        SkASSERT(dst);
+        dst->set(SkScalarRound(fLeft), SkScalarRound(fTop),
+                 SkScalarRound(fRight), SkScalarRound(fBottom));
+    }
+
+    /**
+     *  Set the dst rectangle by rounding "out" this rectangle, choosing the
+     *  SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
+     */
+    void roundOut(SkIRect* dst) const {
+        SkASSERT(dst);
+        dst->set(SkScalarFloor(fLeft), SkScalarFloor(fTop),
+                 SkScalarCeil(fRight), SkScalarCeil(fBottom));
+    }
+
+    /**
+     *  Expand this rectangle by rounding its coordinates "out", choosing the
+     *  floor of top and left, and the ceil of right and bottom. If this rect
+     *  is already on integer coordinates, then it will be unchanged.
+     */
+    void roundOut() {
+        this->set(SkScalarFloorToScalar(fLeft),
+                  SkScalarFloorToScalar(fTop),
+                  SkScalarCeilToScalar(fRight),
+                  SkScalarCeilToScalar(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
+     *  other. When this returns, left <= right && top <= bottom
+     */
+    void sort();
+};
+
+#endif
+
diff --git a/legacy/include/core/SkRefCnt.h b/legacy/include/core/SkRefCnt.h
new file mode 100644
index 0000000..7af0017
--- /dev/null
+++ b/legacy/include/core/SkRefCnt.h
@@ -0,0 +1,181 @@
+
+/*
+ * 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 SkRefCnt_DEFINED
+#define SkRefCnt_DEFINED
+
+#include "SkThread.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.
+*/
+class SK_API SkRefCnt : SkNoncopyable {
+public:
+    /** Default construct, initializing the reference count to 1.
+    */
+    SkRefCnt() : fRefCnt(1) {}
+
+    /**  Destruct, asserting that the reference count is 1.
+    */
+    virtual ~SkRefCnt() {
+#ifdef SK_DEBUG
+        SkASSERT(fRefCnt == 1);
+        fRefCnt = 0;    // illegal value, to catch us if we reuse after delete
+#endif
+    }
+
+    /** Return the reference count.
+    */
+    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);
+        }
+    }
+
+    void validate() const {
+        SkASSERT(fRefCnt > 0);
+    }
+
+private:
+    mutable int32_t fRefCnt;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Helper macro to safely assign one SkRefCnt[TS]* to another, checking for
+    null in on each side of the assignment, and ensuring that ref() is called
+    before unref(), in case the two pointers point to the same object.
+ */
+#define SkRefCnt_SafeAssign(dst, src)   \
+    do {                                \
+        if (src) src->ref();            \
+        if (dst) dst->unref();          \
+        dst = src;                      \
+    } while (0)
+
+
+/** Check if the argument is non-null, and if so, call obj->ref()
+ */
+template <typename T> static inline void SkSafeRef(T* obj) {
+    if (obj) {
+        obj->ref();
+    }
+}
+
+/** Check if the argument is non-null, and if so, call obj->unref()
+ */
+template <typename T> static inline void SkSafeUnref(T* obj) {
+    if (obj) {
+        obj->unref();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Utility class that simply unref's its argument in the destructor.
+ */
+template <typename T> class SkAutoTUnref : SkNoncopyable {
+public:
+    explicit SkAutoTUnref(T* obj = NULL) : fObj(obj) {}
+    ~SkAutoTUnref() { SkSafeUnref(fObj); }
+
+    T* get() const { return fObj; }
+
+    void reset(T* obj) {
+        SkSafeUnref(fObj);
+        fObj = obj;
+    }
+
+    /**
+     *  Return the hosted object (which may be null), transferring ownership.
+     *  The reference count is not modified, and the internal ptr is set to NULL
+     *  so unref() will not be called in our destructor. A subsequent call to
+     *  detach() will do nothing and return null.
+     */
+    T* detach() {
+        T* obj = fObj;
+        fObj = NULL;
+        return obj;
+    }
+
+private:
+    T*  fObj;
+};
+
+class SkAutoUnref : public SkAutoTUnref<SkRefCnt> {
+public:
+    SkAutoUnref(SkRefCnt* obj) : SkAutoTUnref<SkRefCnt>(obj) {}
+};
+
+class SkAutoRef : SkNoncopyable {
+public:
+    SkAutoRef(SkRefCnt* obj) : fObj(obj) { SkSafeRef(obj); }
+    ~SkAutoRef() { SkSafeUnref(fObj); }
+private:
+    SkRefCnt* fObj;
+};
+
+/** Wrapper class for SkRefCnt pointers. This manages ref/unref of a pointer to
+    a SkRefCnt (or subclass) object.
+ */
+template <typename T> class SkRefPtr {
+public:
+    SkRefPtr() : fObj(NULL) {}
+    SkRefPtr(T* obj) : fObj(obj) { SkSafeRef(fObj); }
+    SkRefPtr(const SkRefPtr& o) : fObj(o.fObj) { SkSafeRef(fObj); }
+    ~SkRefPtr() { SkSafeUnref(fObj); }
+
+    SkRefPtr& operator=(const SkRefPtr& rp) {
+        SkRefCnt_SafeAssign(fObj, rp.fObj);
+        return *this;
+    }
+    SkRefPtr& operator=(T* obj) {
+        SkRefCnt_SafeAssign(fObj, obj);
+        return *this;
+    }
+
+    T* get() const { return fObj; }
+    T& operator*() const { return *fObj; }
+    T* operator->() const { return fObj; }
+
+    typedef T* SkRefPtr::*unspecified_bool_type;
+    operator unspecified_bool_type() const {
+        return fObj ? &SkRefPtr::fObj : NULL;
+    }
+
+private:
+    T* fObj;
+};
+
+#endif
+
diff --git a/include/core/SkRefDict.h b/legacy/include/core/SkRefDict.h
similarity index 100%
rename from include/core/SkRefDict.h
rename to legacy/include/core/SkRefDict.h
diff --git a/legacy/include/core/SkRegion.h b/legacy/include/core/SkRegion.h
new file mode 100644
index 0000000..7623b82
--- /dev/null
+++ b/legacy/include/core/SkRegion.h
@@ -0,0 +1,411 @@
+
+/*
+ * Copyright 2005 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 SkRegion_DEFINED
+#define SkRegion_DEFINED
+
+#include "SkRect.h"
+
+class SkPath;
+class SkRgnBuilder;
+
+namespace android {
+    class Region;
+}
+
+#define SkRegion_gEmptyRunHeadPtr   ((SkRegion::RunHead*)-1)
+#define SkRegion_gRectRunHeadPtr    0
+
+/** \class SkRegion
+
+    The SkRegion class encapsulates the geometric region used to specify
+    clipping areas for drawing.
+*/
+class SK_API SkRegion {
+public:
+    typedef int32_t RunType;
+    enum {
+        kRunTypeSentinel = 0x7FFFFFFF
+    };
+    
+    SkRegion();
+    SkRegion(const SkRegion&);
+    explicit SkRegion(const SkIRect&);
+    ~SkRegion();
+
+    SkRegion& operator=(const SkRegion&);
+
+    /**
+     *  Return true if the two regions are equal. i.e. The enclose exactly
+     *  the same area.
+     */
+    bool operator==(const SkRegion& other) const;
+
+    /**
+     *  Return true if the two regions are not equal.
+     */
+    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.
+     */
+    bool set(const SkRegion& src) {
+        SkASSERT(&src);
+        *this = src;
+        return !this->isEmpty();
+    }
+
+    /**
+     *  Swap the contents of this and the specified region. This operation
+     *  is gauarenteed to never fail.
+     */
+    void swap(SkRegion&);
+
+    /** Return true if this region is empty */
+    bool isEmpty() const { return fRunHead == SkRegion_gEmptyRunHeadPtr; }
+
+    /** Return true if this region is a single, non-empty rectangle */
+    bool isRect() const { return fRunHead == SkRegion_gRectRunHeadPtr; }
+
+    /** Return true if this region consists of more than 1 rectangular area */
+    bool isComplex() const { return !this->isEmpty() && !this->isRect(); }
+
+    /**
+     *  Return the bounds of this region. If the region is empty, returns an
+     *  empty rectangle.
+     */
+    const SkIRect& getBounds() const { return fBounds; }
+
+    /**
+     *  Returns true if the region is non-empty, and if so, appends the
+     *  boundary(s) of the region to the specified path.
+     *  If the region is empty, returns false, and path is left unmodified.
+     */
+    bool getBoundaryPath(SkPath* path) const;
+
+    /**
+     *  Set the region to be empty, and return false, since the resulting
+     *  region is empty
+     */
+    bool setEmpty();
+
+    /**
+     *  If rect is non-empty, set this region to that rectangle and return true,
+     *  otherwise set this region to empty and return false.
+     */
+    bool setRect(const SkIRect&);
+
+    /**
+     *  If left < right and top < bottom, set this region to that rectangle and
+     *  return true, otherwise set this region to empty and return false.
+     */
+    bool setRect(int32_t left, int32_t top, int32_t right, int32_t bottom);
+
+    /**
+     *  Set this region to the union of an array of rects. This is generally
+     *  faster than calling region.op(rect, kUnion_Op) in a loop. If count is
+     *  0, then this region is set to the empty region.
+     *  @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.
+     */
+    bool setRegion(const SkRegion&);
+
+    /**
+     *  Set this region to the area described by the path, clipped.
+     *  Return true if the resulting region is non-empty.
+     *  This produces a region that is identical to the pixels that would be
+     *  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.
+     */
+    bool intersects(const SkRegion&) const;
+
+    /**
+     *  Return true if the specified x,y coordinate is inside the region.
+     */
+    bool contains(int32_t x, int32_t y) const;
+
+    /**
+     *  Return true if the specified rectangle is completely inside the region.
+     *  This works for simple (rectangular) and complex regions, and always
+     *  returns the correct result. Note: if either this region or the rectangle
+     *  is empty, contains() returns false.
+     */
+    bool contains(const SkIRect&) const;
+
+    /**
+     *  Return true if the specified region is completely inside the region.
+     *  This works for simple (rectangular) and complex regions, and always
+     *  returns the correct result. Note: if either region is empty, contains()
+     *  returns false.
+     */
+    bool contains(const SkRegion&) const;
+
+    /**
+     *  Return true if this region is a single rectangle (not complex) and the
+     *  specified rectangle is contained by this region. Returning false is not
+     *  a guarantee that the rectangle is not contained by this region, but
+     *  return true is a guarantee that the rectangle is contained by this region.
+     */
+    bool quickContains(const SkIRect& r) const {
+        return this->quickContains(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+
+    /**
+     *  Return true if this region is a single rectangle (not complex) and the
+     *  specified rectangle is contained by this region. Returning false is not
+     *  a guarantee that the rectangle is not contained by this region, but
+     *  return true is a guarantee that the rectangle is contained by this
+     *  region.
+     */
+    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
+     *  intersect, but returning true is a guarantee that they do not.
+     */
+    bool quickReject(const SkIRect& rect) const {
+        return this->isEmpty() || rect.isEmpty() ||
+                !SkIRect::Intersects(fBounds, rect);
+    }
+
+    /**
+     *  Return true if this region, or rgn, is empty, or if their bounds do not
+     *  intersect. Returning false is not a guarantee that they intersect, but
+     *  returning true is a guarantee that they do not.
+     */
+    bool quickReject(const SkRegion& rgn) const {
+        return this->isEmpty() || rgn.isEmpty() ||
+               !SkIRect::Intersects(fBounds, rgn.fBounds);
+    }
+
+    /** Translate the region by the specified (dx, dy) amount. */
+    void translate(int dx, int dy) { this->translate(dx, dy, this); }
+
+    /**
+     *  Translate the region by the specified (dx, dy) amount, writing the
+     *  resulting region into dst. Note: it is legal to pass this region as the
+     *  dst parameter, effectively translating the region in place. If dst is
+     *  null, nothing happens.
+     */
+    void translate(int dx, int dy, SkRegion* dst) const;
+
+    /**
+     *  The logical operations that can be performed when combining two regions.
+     */
+    enum Op {
+        kDifference_Op, //!< subtract the op region from the first region
+        kIntersect_Op,  //!< intersect the two regions
+        kUnion_Op,      //!< union (inclusive-or) the two regions
+        kXOR_Op,        //!< exclusive-or the two regions
+        /** subtract the first region from the op region */
+        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).
+     *  Return true if the resulting region is non-empty.
+     */
+    bool op(int left, int top, int right, int bottom, Op op) {
+        SkIRect rect;
+        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).
+     *  Return true if the resulting region is non-empty.
+     */
+    bool op(const SkRegion& rgn, Op op) { return this->op(*this, rgn, op); }
+
+    /**
+     *  Set this region to the result of applying the Op to the specified
+     *  rectangle and region: this = (rect op rgn).
+     *  Return true if the resulting region is non-empty.
+     */
+    bool op(const SkIRect& rect, const SkRegion& rgn, Op);
+
+    /**
+     *  Set this region to the result of applying the Op to the specified
+     *  region and rectangle: this = (rgn op rect).
+     *  Return true if the resulting region is non-empty.
+     */
+    bool op(const SkRegion& rgn, const SkIRect& rect, Op);
+
+    /**
+     *  Set this region to the result of applying the Op to the specified
+     *  regions: this = (rgna op rgnb).
+     *  Return true if the resulting region is non-empty.
+     */
+    bool op(const SkRegion& rgna, const SkRegion& rgnb, Op op);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    /** Returns a new char* containing the list of rectangles in this region
+     */
+    char* toString();
+#endif
+
+    /**
+     *  Returns the sequence of rectangles, sorted in Y and X, that make up
+     *  this region.
+     */
+    class SK_API Iterator {
+    public:
+        Iterator() : fRgn(NULL), fDone(true) {}
+        Iterator(const SkRegion&);
+        // if we have a region, reset to it and return true, else return false
+        bool rewind();
+        // reset the iterator, using the new region
+        void reset(const SkRegion&);
+        bool done() const { return fDone; }
+        void next();
+        const SkIRect& rect() const { return fRect; }
+        // may return null
+        const SkRegion* rgn() const { return fRgn; }
+
+    private:
+        const SkRegion* fRgn;
+        const RunType*  fRuns;
+        SkIRect         fRect;
+        bool            fDone;
+    };
+
+    /**
+     *  Returns the sequence of rectangles, sorted in Y and X, that make up
+     *  this region intersected with the specified clip rectangle.
+     */
+    class SK_API Cliperator {
+    public:
+        Cliperator(const SkRegion&, const SkIRect& clip);
+        bool done() { return fDone; }
+        void  next();
+        const SkIRect& rect() const { return fRect; }
+
+    private:
+        Iterator    fIter;
+        SkIRect     fClip;
+        SkIRect     fRect;
+        bool        fDone;
+    };
+
+    /**
+     *  Returns the sequence of runs that make up this region for the specified
+     *  Y scanline, clipped to the specified left and right X values.
+     */
+    class Spanerator {
+    public:
+        Spanerator(const SkRegion&, int y, int left, int right);
+        bool next(int* left, int* right);
+
+    private:
+        const SkRegion::RunType* fRuns;
+        int     fLeft, fRight;
+        bool    fDone;
+    };
+
+    /**
+     *  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;
+
+    /**
+     *  Initialized the region from the buffer, returning the number
+     *  of bytes actually read.
+     */
+    uint32_t unflatten(const void* buffer);
+
+    /**
+     *  Returns a reference to a global empty region. Just a convenience for
+     *  callers that need a const empty region.
+     */
+    static const SkRegion& GetEmptyRegion();
+
+    SkDEBUGCODE(void dump() const;)
+    SkDEBUGCODE(void validate() const;)
+    SkDEBUGCODE(static void UnitTest();)
+
+    // expose this to allow for regression test on complex regions
+    SkDEBUGCODE(bool debugSetRuns(const RunType runs[], int count);)
+
+private:
+    enum {
+        kOpCount = kReplace_Op + 1
+    };
+
+    enum {
+        kRectRegionRuns = 6 // need to store a region of a rect [T B L R S S]        
+    };
+
+    friend class android::Region;    // needed for marshalling efficiently
+    void allocateRuns(int count); // allocate space for count runs
+
+    struct RunHead;
+
+    SkIRect     fBounds;
+    RunHead*    fRunHead;
+
+    void            freeRuns();
+    const RunType*  getRuns(RunType tmpStorage[], int* count) const;
+    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);
+
+    friend struct RunHead;
+    friend class Iterator;
+    friend class Spanerator;
+    friend class SkRgnBuilder;
+    friend class SkFlatRegion;
+};
+
+#endif
diff --git a/include/core/SkRelay.h b/legacy/include/core/SkRelay.h
similarity index 100%
rename from include/core/SkRelay.h
rename to legacy/include/core/SkRelay.h
diff --git a/legacy/include/core/SkScalar.h b/legacy/include/core/SkScalar.h
new file mode 100644
index 0000000..71aad98
--- /dev/null
+++ b/legacy/include/core/SkScalar.h
@@ -0,0 +1,347 @@
+
+/*
+ * 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 SkScalar_DEFINED
+#define SkScalar_DEFINED
+
+#include "SkFixed.h"
+#include "SkFloatingPoint.h"
+
+/** \file SkScalar.h
+
+    Types and macros for the data type SkScalar. This is the fractional numeric type
+    that, depending on the compile-time flag SK_SCALAR_IS_FLOAT, may be implemented
+    either as an IEEE float, or as a 16.16 SkFixed. The macros in this file are written
+    to allow the calling code to manipulate SkScalar values without knowing which representation
+    is in effect.
+*/
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+    /** SkScalar is our type for fractional values and coordinates. Depending on
+        compile configurations, it is either represented as an IEEE float, or
+        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
+    */
+    #define SK_Scalar1              (1.0f)
+    /** SK_Scalar1 is defined to be 1/2 represented as an SkScalar
+    */
+    #define SK_ScalarHalf           (0.5f)
+    /** SK_ScalarInfinity is defined to be infinity as an SkScalar
+    */
+    #define SK_ScalarInfinity           (*(const float*)&gIEEEInfinity)
+    /** SK_ScalarMax is defined to be the largest value representable as an SkScalar
+    */
+    #define SK_ScalarMax            (3.402823466e+38f)
+    /** SK_ScalarMin is defined to be the smallest value representable as an SkScalar
+    */
+    #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)
+    /** 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;
+    }
+#ifdef SK_DEBUG
+    /** SkIntToScalar(n) returns its integer argument as an SkScalar
+     *
+     * If we're compiling in DEBUG mode, and can thus afford some extra runtime
+     * cycles, check to make sure that the parameter passed in has not already
+     * been converted to SkScalar.  (A double conversion like this is harmless
+     * for SK_SCALAR_IS_FLOAT, but for SK_SCALAR_IS_FIXED this causes trouble.)
+     *
+     * Note that we need all of these method signatures to properly handle the
+     * various types that we pass into SkIntToScalar() to date:
+     * int, size_t, U8CPU, etc., even though what we really mean is "anything
+     * but a float".
+     */
+    static inline float SkIntToScalar(signed int param) {
+        return (float)param;
+    }
+    static inline float SkIntToScalar(unsigned int param) {
+        return (float)param;
+    }
+    static inline float SkIntToScalar(signed long param) {
+        return (float)param;
+    }
+    static inline float SkIntToScalar(unsigned long param) {
+        return (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)
+         * 2. the parameter was a float instead of an int
+         *
+         * Either way, it's not good.
+         */
+        SkDEBUGFAIL("looks like you passed an SkScalar into SkIntToScalar");
+        return (float)0;
+    }
+#else  // not SK_DEBUG
+    /** SkIntToScalar(n) returns its integer argument as an SkScalar
+    */
+    #define SkIntToScalar(n)        ((float)(n))
+#endif // not SK_DEBUG
+    /** SkFixedToScalar(n) returns its SkFixed argument as an SkScalar
+    */
+    #define SkFixedToScalar(x)      SkFixedToFloat(x)
+    /** SkScalarToFixed(n) returns its SkScalar argument as an SkFixed
+    */
+    #define SkScalarToFixed(x)      SkFloatToFixed(x)
+
+    #define SkScalarToFloat(n)      (n)
+    #define SkFloatToScalar(n)      (n)
+
+    #define SkScalarToDouble(n)      (double)(n)
+    #define SkDoubleToScalar(n)      (float)(n)
+
+    /** SkScalarFraction(x) returns the signed fractional part of the argument
+    */
+    #define SkScalarFraction(x)     sk_float_mod(x, 1.0f)
+
+    #define SkScalarFloorToScalar(x)    sk_float_floor(x)
+    #define SkScalarCeilToScalar(x)     sk_float_ceil(x)
+    #define SkScalarRoundToScalar(x)    sk_float_floor((x) + 0.5f)
+
+    #define SkScalarFloorToInt(x)       sk_float_floor2int(x)
+    #define SkScalarCeilToInt(x)        sk_float_ceil2int(x)
+    #define SkScalarRoundToInt(x)       sk_float_round2int(x)
+
+    /** Returns the absolute value of the specified SkScalar
+    */
+    #define SkScalarAbs(x)          sk_float_abs(x)
+    /** Return x with the sign of y
+     */
+    #define SkScalarCopySign(x, y)  sk_float_copysign(x, y)
+    /** Returns the value pinned between 0 and max inclusive
+    */
+    inline SkScalar SkScalarClampMax(SkScalar x, SkScalar max) {
+        return x < 0 ? 0 : x > max ? max : x;
+    }
+    /** Returns the value pinned between min and max inclusive
+    */
+    inline SkScalar SkScalarPin(SkScalar x, SkScalar min, SkScalar max) {
+        return x < min ? min : x > max ? max : x;
+    }
+    /** Returns the specified SkScalar squared (x*x)
+    */
+    inline SkScalar SkScalarSquare(SkScalar x) { return x * x; }
+    /** Returns the product of two SkScalars
+    */
+    #define SkScalarMul(a, b)       ((float)(a) * (b))
+    /** Returns the product of two SkScalars plus a third SkScalar
+    */
+    #define SkScalarMulAdd(a, b, c) ((float)(a) * (b) + (c))
+    /** Returns the product of a SkScalar and an int rounded to the nearest integer value
+    */
+    #define SkScalarMulRound(a, b) SkScalarRound((float)(a) * (b))
+    /** Returns the product of a SkScalar and an int promoted to the next larger int
+    */
+    #define SkScalarMulCeil(a, b) SkScalarCeil((float)(a) * (b))
+    /** Returns the product of a SkScalar and an int truncated to the next smaller int
+    */
+    #define SkScalarMulFloor(a, b) SkScalarFloor((float)(a) * (b))
+    /** Returns the quotient of two SkScalars (a/b)
+    */
+    #define SkScalarDiv(a, b)       ((float)(a) / (b))
+    /** Returns the mod of two SkScalars (a mod b)
+    */
+    #define SkScalarMod(x,y)        sk_float_mod(x,y)
+    /** Returns the product of the first two arguments, divided by the third argument
+    */
+    #define SkScalarMulDiv(a, b, c) ((float)(a) * (b) / (c))
+    /** Returns the multiplicative inverse of the SkScalar (1/x)
+    */
+    #define SkScalarInvert(x)       (SK_Scalar1 / (x))
+    #define SkScalarFastInvert(x)   (SK_Scalar1 / (x))
+    /** Returns the square root of the SkScalar
+    */
+    #define SkScalarSqrt(x)         sk_float_sqrt(x)
+    /** Returns the average of two SkScalars (a+b)/2
+    */
+    #define SkScalarAve(a, b)       (((a) + (b)) * 0.5f)
+    /** Returns the geometric mean of two SkScalars
+    */
+    #define SkScalarMean(a, b)      sk_float_sqrt((float)(a) * (b))
+    /** Returns one half of the specified SkScalar
+    */
+    #define SkScalarHalf(a)         ((a) * 0.5f)
+
+    #define SK_ScalarSqrt2          1.41421356f
+    #define SK_ScalarPI             3.14159265f
+    #define SK_ScalarTanPIOver8     0.414213562f
+    #define SK_ScalarRoot2Over2     0.707106781f
+
+    #define SkDegreesToRadians(degrees) ((degrees) * (SK_ScalarPI / 180))
+    float SkScalarSinCos(SkScalar radians, SkScalar* cosValue);
+    #define SkScalarSin(radians)    (float)sk_float_sin(radians)
+    #define SkScalarCos(radians)    (float)sk_float_cos(radians)
+    #define SkScalarTan(radians)    (float)sk_float_tan(radians)
+    #define SkScalarASin(val)   (float)sk_float_asin(val)
+    #define SkScalarACos(val)   (float)sk_float_acos(val)
+    #define SkScalarATan2(y, x) (float)sk_float_atan2(y,x)
+    #define SkScalarExp(x)  (float)sk_float_exp(x)
+    #define SkScalarLog(x)  (float)sk_float_log(x)
+
+    inline SkScalar SkMaxScalar(SkScalar a, SkScalar b) { return a > b ? a : b; }
+    inline SkScalar SkMinScalar(SkScalar a, SkScalar b) { return a < b ? a : b; }
+
+    static inline bool SkScalarIsInt(SkScalar x) {
+        return x == (float)(int)x;
+    }
+#else
+    typedef SkFixed SkScalar;
+
+    #define SK_Scalar1              SK_Fixed1
+    #define SK_ScalarHalf           SK_FixedHalf
+    #define SK_ScalarInfinity   SK_FixedMax
+    #define SK_ScalarMax            SK_FixedMax
+    #define SK_ScalarMin            SK_FixedMin
+    #define SK_ScalarNaN            SK_FixedNaN
+    #define SkScalarIsNaN(x)        ((x) == SK_FixedNaN)
+    #define SkScalarIsFinite(x)     ((x) != SK_FixedNaN)
+
+    #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 SkScalarToDouble(n) SkFixedToDouble(n)
+        #define SkDoubleToScalar(n) SkDoubleToFixed(n)
+    #endif
+    #define SkScalarFraction(x)     SkFixedFraction(x)
+
+    #define SkScalarFloorToScalar(x)    SkFixedFloorToFixed(x)
+    #define SkScalarCeilToScalar(x)     SkFixedCeilToFixed(x)
+    #define SkScalarRoundToScalar(x)    SkFixedRoundToFixed(x)
+
+    #define SkScalarFloorToInt(x)       SkFixedFloorToInt(x)
+    #define SkScalarCeilToInt(x)        SkFixedCeilToInt(x)
+    #define SkScalarRoundToInt(x)       SkFixedRoundToInt(x)
+
+    #define SkScalarAbs(x)          SkFixedAbs(x)
+    #define SkScalarCopySign(x, y)  SkCopySign32(x, y)
+    #define SkScalarClampMax(x, max) SkClampMax(x, max)
+    #define SkScalarPin(x, min, max) SkPin32(x, min, max)
+    #define SkScalarSquare(x)       SkFixedSquare(x)
+    #define SkScalarMul(a, b)       SkFixedMul(a, b)
+    #define SkScalarMulAdd(a, b, c) SkFixedMulAdd(a, b, c)
+    #define SkScalarMulRound(a, b)  SkFixedMulCommon(a, b, SK_FixedHalf)
+    #define SkScalarMulCeil(a, b)   SkFixedMulCommon(a, b, SK_Fixed1 - 1)
+    #define SkScalarMulFloor(a, b)  SkFixedMulCommon(a, b, 0)
+    #define SkScalarDiv(a, b)       SkFixedDiv(a, b)
+    #define SkScalarMod(a, b)       SkFixedMod(a, b)
+    #define SkScalarMulDiv(a, b, c) SkMulDiv(a, b, c)
+    #define SkScalarInvert(x)       SkFixedInvert(x)
+    #define SkScalarFastInvert(x)   SkFixedFastInvert(x)
+    #define SkScalarSqrt(x)         SkFixedSqrt(x)
+    #define SkScalarAve(a, b)       SkFixedAve(a, b)
+    #define SkScalarMean(a, b)      SkFixedMean(a, b)
+    #define SkScalarHalf(a)         ((a) >> 1)
+
+    #define SK_ScalarSqrt2          SK_FixedSqrt2
+    #define SK_ScalarPI             SK_FixedPI
+    #define SK_ScalarTanPIOver8     SK_FixedTanPIOver8
+    #define SK_ScalarRoot2Over2     SK_FixedRoot2Over2
+
+    #define SkDegreesToRadians(degrees)     SkFractMul(degrees, SK_FractPIOver180)
+    #define SkScalarSinCos(radians, cosPtr) SkFixedSinCos(radians, cosPtr)
+    #define SkScalarSin(radians)    SkFixedSin(radians)
+    #define SkScalarCos(radians)    SkFixedCos(radians)
+    #define SkScalarTan(val)        SkFixedTan(val)
+    #define SkScalarASin(val)       SkFixedASin(val)
+    #define SkScalarACos(val)       SkFixedACos(val)
+    #define SkScalarATan2(y, x)     SkFixedATan2(y,x)
+    #define SkScalarExp(x)          SkFixedExp(x)
+    #define SkScalarLog(x)          SkFixedLog(x)
+
+    #define SkMaxScalar(a, b)       SkMax32(a, b)
+    #define SkMinScalar(a, b)       SkMin32(a, b)
+
+    static inline bool SkScalarIsInt(SkFixed x) {
+        return 0 == (x & 0xffff);
+    }
+#endif
+
+// DEPRECATED : use ToInt or ToScalar variant
+#define SkScalarFloor(x)    SkScalarFloorToInt(x)
+#define SkScalarCeil(x)     SkScalarCeilToInt(x)
+#define SkScalarRound(x)    SkScalarRoundToInt(x)
+
+/**
+ *  Returns -1 || 0 || 1 depending on the sign of value:
+ *  -1 if x < 0
+ *   0 if x == 0
+ *   1 if x > 0
+ */
+static inline int SkScalarSignAsInt(SkScalar x) {
+    return x < 0 ? -1 : (x > 0);
+}
+
+// Scalar result version of above
+static inline SkScalar SkScalarSignAsScalar(SkScalar x) {
+    return x < 0 ? -SK_Scalar1 : ((x > 0) ? SK_Scalar1 : 0);
+}
+
+#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;
+}
+
+static inline bool SkScalarNearlyEqual(SkScalar x, SkScalar y,
+                                     SkScalar tolerance = SK_ScalarNearlyZero) {
+    SkASSERT(tolerance > 0);
+    return SkScalarAbs(x-y) < tolerance;
+}
+
+/** Linearly interpolate between A and B, based on t.
+    If t is 0, return A
+    If t is 1, return B
+    else interpolate.
+    t must be [0..SK_Scalar1]
+*/
+static inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) {
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+    return A + SkScalarMul(B - A, t);
+}
+
+/** 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
+    to change the multiplier for thickness in fakeBold; therefore it assumes
+    the number of pairs (length) will be small, and a linear search is used.
+    Repeated keys are allowed for discontinuous functions (so long as keys is
+    monotonically increasing), and if key is the value of a repeated scalar in
+    keys, the first one will be used.  However, that may change if a binary
+    search is used.
+*/
+SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[],
+                            const SkScalar values[], int length);
+
+#endif
diff --git a/legacy/include/core/SkScalarCompare.h b/legacy/include/core/SkScalarCompare.h
new file mode 100644
index 0000000..0842fdd
--- /dev/null
+++ b/legacy/include/core/SkScalarCompare.h
@@ -0,0 +1,39 @@
+
+/*
+ * 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 SkScalarCompare_DEFINED
+#define SkScalarCompare_DEFINED
+
+#include "SkFloatBits.h"
+#include "SkRect.h"
+
+/** Skia can spend a lot of time just comparing scalars (e.g. quickReject).
+    When scalar==fixed, this is very fast, and when scalar==hardware-float, this
+    is also reasonable, but if scalar==software-float, then each compare can be
+    a function call and take real time. To account for that, we have the flag
+    SK_SCALAR_SLOW_COMPARES.
+
+    If this is defined, we have a special trick where we quickly convert floats
+    to a 2's compliment form, and then treat them as signed 32bit integers. In
+    this form we lose a few subtlties (e.g. NaNs always comparing false) but
+    we gain the speed of integer compares.
+ */
+
+#ifdef SK_SCALAR_SLOW_COMPARES
+    typedef int32_t SkScalarCompareType;
+    typedef SkIRect SkRectCompareType;
+    #define SkScalarToCompareType(x)    SkScalarAs2sCompliment(x)
+#else
+    typedef SkScalar SkScalarCompareType;
+    typedef SkRect SkRectCompareType;
+    #define SkScalarToCompareType(x)    (x)
+#endif
+
+#endif
+
diff --git a/include/core/SkScalerContext.h b/legacy/include/core/SkScalerContext.h
similarity index 100%
rename from include/core/SkScalerContext.h
rename to legacy/include/core/SkScalerContext.h
diff --git a/include/core/SkScan.h b/legacy/include/core/SkScan.h
similarity index 100%
rename from include/core/SkScan.h
rename to legacy/include/core/SkScan.h
diff --git a/legacy/include/core/SkShader.h b/legacy/include/core/SkShader.h
new file mode 100644
index 0000000..7c5be06
--- /dev/null
+++ b/legacy/include/core/SkShader.h
@@ -0,0 +1,314 @@
+
+/*
+ * 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 SkShader_DEFINED
+#define SkShader_DEFINED
+
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkMask.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+
+class SkPath;
+
+/** \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.
+ */
+class SK_API SkShader : public SkFlattenable {
+public:
+            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.
+     */
+    bool getLocalMatrix(SkMatrix* localM) const;
+
+    /**
+     *  Set the shader's local matrix.
+     *  @param localM   The shader's new local matrix.
+     */
+    void setLocalMatrix(const SkMatrix& localM);
+
+    /**
+     *  Reset the shader's local matrix to identity.
+     */
+    void resetLocalMatrix();
+
+    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
+
+        kTileModeCount
+    };
+
+    // override these in your subclass
+
+    enum Flags {
+        //!< set if all of the colors will be opaque
+        kOpaqueAlpha_Flag  = 0x01,
+
+        //! set if this shader's shadeSpan16() method can be called
+        kHasSpan16_Flag = 0x02,
+
+        /** Set this bit if the shader's native data type is instrinsically 16
+            bit, meaning that calling the 32bit shadeSpan() entry point will
+            mean the the impl has to up-sample 16bit data into 32bit. Used as a
+            a means of clearing a dither request if the it will have no effect
+        */
+        kIntrinsicly16_Flag = 0x04,
+
+        /** set (after setContext) if the spans only vary in X (const in Y).
+            e.g. an Nx1 bitmap that is being tiled in Y, or a linear-gradient
+            that varies from left-to-right. This flag specifies this for
+            shadeSpan().
+         */
+        kConstInY32_Flag = 0x08,
+
+        /** same as kConstInY32_Flag, but is set if this is true for shadeSpan16
+            which may not always be the case, since shadeSpan16 may be
+            predithered, which would mean it was not const in Y, even though
+            the 32bit shadeSpan() would be const.
+         */
+        kConstInY16_Flag = 0x10
+    };
+
+    /**
+     *  Called sometimes before drawing with this shader. Return the type of
+     *  alpha your shader will return. The default implementation returns 0.
+     *  Your subclass should override if it can (even sometimes) report a
+     *  non-zero value, since that will enable various blitters to perform
+     *  faster.
+     */
+    virtual uint32_t getFlags() { return 0; }
+
+    /**
+     *  Returns true if the shader is guaranteed to produce only opaque
+     *  colors, subject to the SkPaint using the shader to apply an opaque
+     *  alpha value. Subclasses should override this to allow some
+     *  optimizations.  isOpaque() can be called at any time, unlike getFlags,
+     *  which only works properly when the context is set.
+     */
+    virtual bool isOpaque() const { return false; }
+
+    /**
+     *  Return the alpha associated with the data returned by shadeSpan16(). If
+     *  kHasSpan16_Flag is not set, this value is meaningless.
+     */
+    virtual uint8_t getSpan16Alpha() const { return fPaintAlpha; }
+
+    /**
+     *  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.
+     */
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
+                            const SkMatrix& matrix);
+
+    /**
+     *  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;
+
+    /**
+     *  Called only for 16bit devices when getFlags() returns
+     *  kOpaqueAlphaFlag | kHasSpan16_Flag
+     */
+    virtual void shadeSpan16(int x, int y, uint16_t[], int count);
+
+    /**
+     *  Similar to shadeSpan, but only returns the alpha-channel for a span.
+     *  The default implementation calls shadeSpan() and then extracts the alpha
+     *  values from the returned colors.
+     */
+    virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count);
+
+    /**
+     *  Helper function that returns true if this shader's shadeSpan16() method
+     *  can be called.
+     */
+    bool canCallShadeSpan16() {
+        return SkShader::CanCallShadeSpan16(this->getFlags());
+    }
+
+    /**
+     *  Helper to check the flags to know if it is legal to call shadeSpan16()
+     */
+    static bool CanCallShadeSpan16(uint32_t flags) {
+        return (flags & kHasSpan16_Flag) != 0;
+    }
+
+    /**
+     *  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
+     */
+    enum BitmapType {
+        kNone_BitmapType,   //<! Shader is not represented as a bitmap
+        kDefault_BitmapType,//<! Access bitmap using local coords transformed
+                            //   by matrix. No extras
+        kRadial_BitmapType, //<! Access bitmap by transforming local coordinates
+                            //   by the matrix and taking the distance of result
+                            //   from  (0,0) as bitmap column. Bitmap is 1 pixel
+                            //   tall. No extras
+        kSweep_BitmapType,  //<! Access bitmap by transforming local coordinates
+                            //   by the matrix and taking the angle of result
+                            //   to (0,0) as bitmap x coord, where angle = 0 is
+                            //   bitmap left edge of bitmap = 2pi is the
+                            //   right edge. Bitmap is 1 pixel tall. No extras
+        kTwoPointRadial_BitmapType,
+                            //<! Matrix transforms to space where (0,0) is
+                            //   the center of the starting circle.  The second
+                            //   circle will be centered (x, 0) where x  may be
+                            //   0. The post-matrix space is normalized such
+                            //   that 1 is the second radius - first radius.
+                            //   Three extra parameters are returned:
+                            //      0: x-offset of second circle center
+                            //         to first.
+                            //      1: radius of first circle in post-matrix
+                            //         space
+                            //      2: the second radius minus the first radius
+                            //         in pre-transformed space.
+
+       kLast_BitmapType = kTwoPointRadial_BitmapType
+    };
+    /** Optional methods for shaders that can pretend to be a bitmap/texture
+        to play along with opengl. Default just returns kNone_BitmapType and
+        ignores the out parameters.
+
+        @param outTexture if non-NULL will be the bitmap representing the shader
+                          after return.
+        @param outMatrix  if non-NULL will be the matrix to apply to vertices
+                          to access the bitmap after return.
+        @param xy         if non-NULL will be the tile modes that should be
+                          used to access the bitmap after return.
+        @param twoPointRadialParams Two extra return values needed for two point
+                                    radial bitmaps. The first is the x-offset of
+                                    the second point and the second is the radius
+                                    about the first point.
+    */
+    virtual BitmapType asABitmap(SkBitmap* outTexture, SkMatrix* outMatrix,
+                         TileMode xy[2], SkScalar* twoPointRadialParams) const;
+
+    /**
+     *  If the shader subclass can be represented as a gradient, asAGradient
+     *  returns the matching GradientType enum (or kNone_GradientType if it
+     *  cannot). Also, if info is not null, asAGradient populates info with
+     *  the relevant (see below) parameters for the gradient.  fColorCount
+     *  is both an input and output parameter.  On input, it indicates how
+     *  many entries in fColors and fColorOffsets can be used, if they are
+     *  non-NULL.  After asAGradient has run, fColorCount indicates how
+     *  many color-offset pairs there are in the gradient.  If there is
+     *  insufficient space to store all of the color-offset pairs, fColors
+     *  and fColorOffsets will not be altered.  fColorOffsets specifies
+     *  where on the range of 0 to 1 to transition to the given color.
+     *  The meaning of fPoint and fRadius is dependant on the type of gradient.
+     *
+     *  None:
+     *      info is ignored.
+     *  Color:
+     *      fColorOffsets[0] is meaningless.
+     *  Linear:
+     *      fPoint[0] and fPoint[1] are the end-points of the gradient
+     *  Radial:
+     *      fPoint[0] and fRadius[0] are the center and radius
+     *  Radial2:
+     *      fPoint[0] and fRadius[0] are the center and radius of the 1st circle
+     *      fPoint[1] and fRadius[1] are the center and radius of the 2nd circle
+     *  Sweep:
+     *      fPoint[0] is the center of the sweep.
+     */
+
+    enum GradientType {
+        kNone_GradientType,
+        kColor_GradientType,
+        kLinear_GradientType,
+        kRadial_GradientType,
+        kRadial2_GradientType,
+        kSweep_GradientType,
+        kLast_GradientType = kSweep_GradientType
+    };
+
+    struct GradientInfo {
+        int         fColorCount;    //!< In-out parameter, specifies passed size
+                                    //   of fColors/fColorOffsets on input, and
+                                    //   actual number of colors/offsets on
+                                    //   output.
+        SkColor*    fColors;        //!< The colors in the gradient.
+        SkScalar*   fColorOffsets;  //!< The unit offset for color transitions.
+        SkPoint     fPoint[2];      //!< Type specific, see above.
+        SkScalar    fRadius[2];     //!< Type specific, see above.
+        TileMode    fTileMode;      //!< The tile mode used.
+    };
+
+    virtual GradientType asAGradient(GradientInfo* info) 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.
+    */
+    static SkShader* CreateBitmapShader(const SkBitmap& src,
+                                        TileMode tmx, TileMode tmy);
+
+    virtual void flatten(SkFlattenableWriteBuffer& ) SK_OVERRIDE;
+protected:
+    enum MatrixClass {
+        kLinear_MatrixClass,            // no perspective
+        kFixedStepInX_MatrixClass,      // fast perspective, need to call fixedStepInX() each scanline
+        kPerspective_MatrixClass        // slow perspective, need to mappoints each pixel
+    };
+    static MatrixClass ComputeMatrixClass(const SkMatrix&);
+
+    // These can be called by your subclass after setContext() has been called
+    uint8_t             getPaintAlpha() const { return fPaintAlpha; }
+    SkBitmap::Config    getDeviceConfig() const { return (SkBitmap::Config)fDeviceConfig; }
+    const SkMatrix&     getTotalInverse() const { return fTotalInverse; }
+    MatrixClass         getInverseClass() const { return (MatrixClass)fTotalInverseClass; }
+
+    SkShader(SkFlattenableReadBuffer& );
+private:
+    SkMatrix*           fLocalMatrix;
+    SkMatrix            fTotalInverse;
+    uint8_t             fPaintAlpha;
+    uint8_t             fDeviceConfig;
+    uint8_t             fTotalInverseClass;
+    SkDEBUGCODE(SkBool8 fInSession;)
+
+    static SkShader* CreateBitmapShader(const SkBitmap& src,
+                                        TileMode, TileMode,
+                                        void* storage, size_t storageSize);
+    friend class SkAutoBitmapShaderInstall;
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
+
diff --git a/include/core/SkShape.h b/legacy/include/core/SkShape.h
similarity index 100%
rename from include/core/SkShape.h
rename to legacy/include/core/SkShape.h
diff --git a/legacy/include/core/SkSize.h b/legacy/include/core/SkSize.h
new file mode 100644
index 0000000..12dbeae
--- /dev/null
+++ b/legacy/include/core/SkSize.h
@@ -0,0 +1,112 @@
+
+/*
+ * 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 SkSize_DEFINED
+#define SkSize_DEFINED
+
+template <typename T> struct SkTSize {
+    T fWidth;
+    T fHeight;
+
+    static SkTSize Make(T w, T h) {
+        SkTSize s;
+        s.fWidth = w;
+        s.fHeight = h;
+        return s;
+    }
+
+    void set(T w, T h) {
+        fWidth = w;
+        fHeight = h;
+    }
+
+    /** Returns true iff fWidth == 0 && fHeight == 0
+     */
+    bool isZero() const {
+        return 0 == fWidth && 0 == fHeight;
+    }
+
+    /** Returns true if either widht or height are <= 0 */
+    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; }
+    
+    /** If width or height is < 0, it is set to 0 */
+    void clampNegToZero() {
+        if (fWidth < 0) {
+            fWidth = 0;
+        }
+        if (fHeight < 0) {
+            fHeight = 0;
+        }
+    }
+    
+    bool equals(T w, T h) const {
+        return fWidth == w && fHeight == h;
+    }
+};
+
+template <typename T>
+static inline bool operator==(const SkTSize<T>& a, const SkTSize<T>& b) {
+    return a.fWidth == b.fWidth && a.fHeight == b.fHeight;
+}
+
+template <typename T>
+static inline bool operator!=(const SkTSize<T>& a, const SkTSize<T>& b) {
+    return !(a == b);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef SkTSize<int32_t> SkISize;
+
+#include "SkScalar.h"
+
+struct SkSize : public SkTSize<SkScalar> {
+    static SkSize Make(SkScalar w, SkScalar h) {
+        SkSize s;
+        s.fWidth = w;
+        s.fHeight = h;
+        return s;
+    }
+    
+    
+    SkSize& operator=(const SkISize& src) {
+        this->set(SkIntToScalar(src.fWidth), SkIntToScalar(src.fHeight));
+        return *this;
+    }
+
+    SkISize toRound() const {
+        SkISize s;
+        s.set(SkScalarRound(fWidth), SkScalarRound(fHeight));
+        return s;
+    }
+
+    SkISize toCeil() const {
+        SkISize s;
+        s.set(SkScalarCeil(fWidth), SkScalarCeil(fHeight));
+        return s;
+    }
+
+    SkISize toFloor() const {
+        SkISize s;
+        s.set(SkScalarFloor(fWidth), SkScalarFloor(fHeight));
+        return s;
+    }
+};
+
+#endif
diff --git a/legacy/include/core/SkStream.h b/legacy/include/core/SkStream.h
new file mode 100644
index 0000000..67512d7
--- /dev/null
+++ b/legacy/include/core/SkStream.h
@@ -0,0 +1,329 @@
+
+/*
+ * 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_DEFINED
+#define SkStream_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+class SkData;
+
+class SK_API SkStream : public SkRefCnt {
+public:
+    virtual ~SkStream();
+    /** Called to rewind to the beginning of the stream. If this cannot be
+        done, return false.
+    */
+    virtual bool rewind() = 0;
+    /** If this stream represents a file, this method returns the file's name.
+        If it does not, it returns NULL (the default behavior).
+    */
+    virtual const char* getFileName();
+    /** Called to read or skip size number of bytes.
+        If buffer is NULL and size > 0, skip that many bytes, returning how many were skipped.
+        If buffer is NULL and size == 0, return the total length of the stream.
+        If buffer != NULL, copy the requested number of bytes into buffer, returning how many were copied.
+        @param buffer   If buffer is NULL, ignore and just skip size bytes, otherwise copy size bytes into buffer
+        @param size The number of bytes to skip or copy
+        @return bytes read on success
+    */
+    virtual size_t read(void* buffer, size_t size) = 0;
+
+    /** 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.
+    */
+    size_t skip(size_t bytes);
+
+    /** If the stream is backed by RAM, this method returns the starting
+        address for the data. If not (i.e. it is backed by a file or other
+        structure), this method returns NULL.
+        The default implementation returns NULL.
+    */
+    virtual const void* getMemoryBase();
+
+    int8_t   readS8();
+    int16_t  readS16();
+    int32_t  readS32();
+
+    uint8_t  readU8() { return (uint8_t)this->readS8(); }
+    uint16_t readU16() { return (uint16_t)this->readS16(); }
+    uint32_t readU32() { return (uint32_t)this->readS32(); }
+
+    bool     readBool() { return this->readU8() != 0; }
+    SkScalar readScalar();
+    size_t   readPackedUInt();
+};
+
+class SK_API SkWStream : SkNoncopyable {
+public:
+    virtual ~SkWStream();
+
+    /** Called to write bytes to a SkWStream. Returns true on success
+        @param buffer the address of at least size bytes to be written to the stream
+        @param size The number of bytes in buffer to write to the stream
+        @return true on success
+    */
+    virtual bool write(const void* buffer, size_t size) = 0;
+    virtual void newline();
+    virtual void flush();
+
+    // helpers
+    
+    bool    write8(U8CPU);
+    bool    write16(U16CPU);
+    bool    write32(uint32_t);
+
+    bool    writeText(const char text[]);
+    bool    writeDecAsText(int32_t);
+    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*);
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkString.h"
+
+struct SkFILE;
+
+/** A stream that reads from a FILE*, which is opened in the constructor and
+    closed in the destructor
+ */
+class SkFILEStream : public SkStream {
+public:
+    /** Initialize the stream by calling fopen on the specified path. Will be
+        closed in the destructor.
+     */
+    explicit SkFILEStream(const char path[] = NULL);
+    virtual ~SkFILEStream();
+
+    /** Returns true if the current path could be opened.
+    */
+    bool isValid() const { return fFILE != NULL; }
+    /** Close the current file, and open a new file with the specified
+        path. If path is NULL, just close the current file.
+    */
+    void setPath(const char path[]);
+
+    virtual bool rewind() SK_OVERRIDE;
+    virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
+    virtual const char* getFileName() SK_OVERRIDE;
+
+private:
+    SkFILE*     fFILE;
+    SkString    fName;
+};
+
+/** A stream that reads from a file descriptor
+ */
+class SkFDStream : public SkStream {
+public:
+    /** 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;
+};
+
+class SkMemoryStream : public SkStream {
+public:
+    SkMemoryStream();
+    /** We allocate (and free) the memory. Write to it via getMemoryBase()
+    */
+    SkMemoryStream(size_t length);
+    /** if copyData is true, the stream makes a private copy of the data
+    */
+    SkMemoryStream(const void* data, size_t length, bool copyData = false);
+    virtual ~SkMemoryStream();
+
+    /** Resets the stream to the specified data and length,
+        just like the constructor.
+        if copyData is true, the stream makes a private copy of the data
+    */
+    virtual void setMemory(const void* data, size_t length,
+                           bool copyData = false);
+    /** Replace any memory buffer with the specified buffer. The caller
+        must have allocated data with sk_malloc or sk_realloc, since it
+        will be freed with sk_free.
+    */
+    void setMemoryOwned(const void* data, size_t length);
+
+    /**
+     *  Return the stream's data in a SkData. The caller must call unref() when
+     *  it is finished using the data.
+     */
+    SkData* copyToData() const;
+
+    /**
+     *  Use the specified data as the memory for this stream. The stream will
+     *  call ref() on the data (assuming it is not null). The function returns
+     *  the data parameter as a convenience.
+     */
+    SkData* setData(SkData*);
+
+    void skipToAlign4();
+    virtual bool rewind() SK_OVERRIDE;
+    virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
+    virtual const void* getMemoryBase() SK_OVERRIDE;
+    const void* getAtPos();
+    size_t seek(size_t offset);
+    size_t peek() const { return fOffset; }
+    
+private:
+    SkData* fData;
+    size_t  fOffset;
+};
+
+/** \class SkBufferStream
+    This is a wrapper class that adds buffering to another stream.
+    The caller can provide the buffer, or ask SkBufferStream to allocated/free
+    it automatically.
+*/
+class SkBufferStream : public SkStream {
+public:
+    /** 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.
+        The proxy stream is referenced, and will be unreferenced in when the
+        bufferstream is destroyed.
+    */
+    SkBufferStream(SkStream* proxy, size_t bufferSize = 0);
+    /** Provide the stream to be buffered (proxy), and a buffer and size to be used.
+        This buffer is owned by the caller, and must be at least bufferSize bytes big.
+        Passing NULL for buffer will cause the buffer to be allocated/freed automatically.
+        If buffer is not NULL, it is an error for bufferSize to be 0.
+     The proxy stream is referenced, and will be unreferenced in when the
+     bufferstream is destroyed.
+    */
+    SkBufferStream(SkStream* proxy, void* buffer, size_t bufferSize);
+    virtual ~SkBufferStream();
+
+    virtual bool        rewind() SK_OVERRIDE;
+    virtual const char* getFileName() SK_OVERRIDE;
+    virtual size_t      read(void* buffer, size_t size) SK_OVERRIDE;
+    virtual const void* getMemoryBase() SK_OVERRIDE;
+
+private:
+    enum {
+        kDefaultBufferSize  = 128
+    };
+    // illegal
+    SkBufferStream(const SkBufferStream&);
+    SkBufferStream& operator=(const SkBufferStream&);
+
+    SkStream*   fProxy;
+    char*       fBuffer;
+    size_t      fOrigBufferSize, fBufferSize, fBufferOffset;
+    bool        fWeOwnTheBuffer;
+
+    void    init(void*, size_t);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkFILEWStream : public SkWStream {
+public:
+            SkFILEWStream(const char path[]);
+    virtual ~SkFILEWStream();
+
+    /** Returns true if the current path could be opened.
+    */
+    bool isValid() const { return fFILE != NULL; }
+
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
+    virtual void flush() SK_OVERRIDE;
+private:
+    SkFILE* fFILE;
+};
+
+class SkMemoryWStream : public SkWStream {
+public:
+    SkMemoryWStream(void* buffer, size_t size);
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
+    size_t bytesWritten() const { return fBytesWritten; }
+
+private:
+    char*   fBuffer;
+    size_t  fMaxLength;
+    size_t  fBytesWritten;
+};
+
+class SK_API SkDynamicMemoryWStream : public SkWStream {
+public:
+    SkDynamicMemoryWStream();
+    virtual ~SkDynamicMemoryWStream();
+
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
+    // random access write
+    // modifies stream and returns true if offset + size is less than or equal to getOffset()
+    bool write(const void* buffer, size_t offset, size_t size);
+    bool read(void* buffer, size_t offset, size_t size);
+    size_t getOffset() const { return fBytesWritten; }
+    size_t bytesWritten() const { return fBytesWritten; }
+
+    // copy what has been written to the stream into dst
+    void copyTo(void* dst) const;
+
+    /**
+     *  Return a copy of the data written so far. This call is responsible for
+     *  calling unref() when they are finished with the data.
+     */
+    SkData* copyToData() const;
+
+    // reset the stream to its original state
+    void reset();
+    void padToAlign4();
+private:
+    struct Block;
+    Block*  fHead;
+    Block*  fTail;
+    size_t  fBytesWritten;
+    mutable SkData* fCopy;  // is invalidated if we write after it is created
+
+    void invalidateCopy();
+};
+
+
+class SkDebugWStream : public SkWStream {
+public:
+    // overrides
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
+    virtual void newline() SK_OVERRIDE;
+};
+
+// for now
+typedef SkFILEStream SkURLStream;
+
+#endif
diff --git a/legacy/include/core/SkString.h b/legacy/include/core/SkString.h
new file mode 100644
index 0000000..3fa367b
--- /dev/null
+++ b/legacy/include/core/SkString.h
@@ -0,0 +1,196 @@
+
+/*
+ * 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 SkString_DEFINED
+#define SkString_DEFINED
+
+#include "SkScalar.h"
+
+/*  Some helper functions for C strings
+*/
+
+bool SkStrStartsWith(const char string[], const char prefix[]);
+bool SkStrEndsWith(const char string[], const char suffix[]);
+int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
+
+#define SkStrAppendS32_MaxSize  11
+char*   SkStrAppendS32(char buffer[], int32_t);
+#define SkStrAppendS64_MaxSize  20
+char*   SkStrAppendS64(char buffer[], int64_t, int minDigits);
+
+/**
+ *  Floats have at most 8 significant digits, so we limit our %g to that.
+ *  However, the total string could be 15 characters: -1.2345678e-005
+ *
+ *  In theory we should only expect up to 2 digits for the exponent, but on
+ *  some platforms we have seen 3 (as in the example above).
+ */
+#define SkStrAppendScalar_MaxSize  15
+
+/**
+ *  Write the scaler in decimal format into buffer, and return a pointer to
+ *  the next char after the last one written. Note: a terminating 0 is not
+ *  written into buffer, which must be at least SkStrAppendScalar_MaxSize.
+ *  Thus if the caller wants to add a 0 at the end, buffer must be at least
+ *  SkStrAppendScalar_MaxSize + 1 bytes large.
+ */
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkStrAppendScalar SkStrAppendFloat
+#else
+    #define SkStrAppendScalar SkStrAppendFixed
+#endif
+
+#ifdef SK_CAN_USE_FLOAT
+char* SkStrAppendFloat(char buffer[], float);
+#endif
+char* SkStrAppendFixed(char buffer[], SkFixed);
+
+/** \class SkString
+
+    Light weight class for managing strings. Uses reference
+    counting to make string assignments and copies very fast
+    with no extra RAM cost. Assumes UTF8 encoding.
+*/
+class SkString {
+public:
+                SkString();
+    explicit    SkString(size_t len);
+    explicit    SkString(const char text[]);
+                SkString(const char text[], size_t len);
+                SkString(const SkString&);
+                ~SkString();
+
+    bool        isEmpty() const { return 0 == fRec->fLength; }
+    size_t      size() const { return (size_t) fRec->fLength; }
+    const char* c_str() const { return fRec->data(); }
+    char operator[](size_t n) const { return this->c_str()[n]; }
+
+    bool equals(const SkString&) const;
+    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 endsWith(const char suffix[]) const {
+        return SkStrEndsWith(fRec->data(), suffix);
+    }
+
+    friend bool operator==(const SkString& a, const SkString& b) {
+        return a.equals(b);
+    }
+    friend bool operator!=(const SkString& a, const SkString& b) {
+        return !a.equals(b);
+    }
+
+    // these methods edit the string
+
+    SkString& operator=(const SkString&);
+    SkString& operator=(const char text[]);
+
+    char* writable_str();
+    char& operator[](size_t n) { return this->writable_str()[n]; }
+
+    void reset();
+    void resize(size_t len) { this->set(NULL, len); }
+    void set(const SkString& src) { *this = src; }
+    void set(const char text[]);
+    void set(const char text[], size_t len);
+    void setUTF16(const uint16_t[]);
+    void setUTF16(const uint16_t[], size_t len);
+
+    void insert(size_t offset, const SkString& src) { this->insert(offset, src.c_str(), src.size()); }
+    void insert(size_t offset, const char text[]);
+    void insert(size_t offset, const char text[], size_t len);
+    void insertUnichar(size_t offset, SkUnichar);
+    void insertS32(size_t offset, int32_t value);
+    void insertS64(size_t offset, int64_t value, int minDigits = 0);
+    void insertHex(size_t offset, uint32_t value, int minDigits = 0);
+    void insertScalar(size_t offset, SkScalar);
+
+    void append(const SkString& str) { this->insert((size_t)-1, str); }
+    void append(const char text[]) { this->insert((size_t)-1, text); }
+    void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); }
+    void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); }
+    void appendS32(int32_t value) { this->insertS32((size_t)-1, value); }
+    void appendS64(int64_t value, int minDigits = 0) { this->insertS64((size_t)-1, value, minDigits); }
+    void appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); }
+    void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); }
+
+    void prepend(const SkString& str) { this->insert(0, str); }
+    void prepend(const char text[]) { this->insert(0, text); }
+    void prepend(const char text[], size_t len) { this->insert(0, text, len); }
+    void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); }
+    void prependS32(int32_t value) { this->insertS32(0, value); }
+    void prependS64(int32_t value, int minDigits = 0) { this->insertS64(0, value, minDigits); }
+    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 remove(size_t offset, size_t length);
+
+    SkString& operator+=(const SkString& s) { this->append(s); return *this; }
+    SkString& operator+=(const char text[]) { this->append(text); return *this; }
+    SkString& operator+=(const char c) { this->append(&c, 1); return *this; }
+
+    /**
+     *  Swap contents between this and other. This function is guaranteed
+     *  to never fail or throw.
+     */
+    void swap(SkString& other);
+
+private:
+    struct Rec {
+    public:
+        size_t      fLength;
+        int32_t     fRefCnt;
+        char        fBeginningOfData;
+
+        char* data() { return &fBeginningOfData; }
+        const char* data() const { return &fBeginningOfData; }
+    };
+    Rec* fRec;
+
+#ifdef SK_DEBUG
+    const char* fStr;
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+    static const Rec gEmptyRec;
+    static Rec* AllocRec(const char text[], size_t len);
+    static Rec* RefRec(Rec*);
+};
+
+class SkAutoUCS2 {
+public:
+    SkAutoUCS2(const char utf8[]);
+    ~SkAutoUCS2();
+
+    /** This returns the number of ucs2 characters
+    */
+    int count() const { return fCount; }
+
+    /** This returns a null terminated ucs2 string
+    */
+    const uint16_t* getUCS2() const { return fUCS2; }
+
+private:
+    int         fCount;
+    uint16_t*   fUCS2;
+};
+
+/// Creates a new string and writes into it using a printf()-style format.
+SkString SkStringPrintf(const char* format, ...);
+
+#endif
diff --git a/include/core/SkStroke.h b/legacy/include/core/SkStroke.h
similarity index 100%
rename from include/core/SkStroke.h
rename to legacy/include/core/SkStroke.h
diff --git a/legacy/include/core/SkTArray.h b/legacy/include/core/SkTArray.h
new file mode 100644
index 0000000..577e860
--- /dev/null
+++ b/legacy/include/core/SkTArray.h
@@ -0,0 +1,415 @@
+/*
+ * 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 SkTArray_DEFINED
+#define SkTArray_DEFINED
+
+#include <new>
+#include "SkTypes.h"
+#include "SkTemplates.h"
+
+template <typename T, bool MEM_COPY = false> class SkTArray;
+
+namespace SkTArrayExt {
+
+template<typename T>
+inline void copy(SkTArray<T, true>* self, const T* array) {
+    memcpy(self->fMemArray, array, self->fCount * sizeof(T));
+}
+template<typename T>
+inline void copyAndDelete(SkTArray<T, true>* self, char* newMemArray) {
+    memcpy(newMemArray, self->fMemArray, self->fCount * sizeof(T));
+}
+
+template<typename T>
+inline void copy(SkTArray<T, false>* self, const T* array) {
+    for (int i = 0; i < self->fCount; ++i) {
+        new (self->fItemArray + i) T(array[i]);
+    }
+}
+template<typename T>
+inline void copyAndDelete(SkTArray<T, false>* self, char* newMemArray) {
+    for (int i = 0; i < self->fCount; ++i) {
+        new (newMemArray + sizeof(T) * i) T(self->fItemArray[i]);
+        self->fItemArray[i].~T();
+    }
+}
+
+}
+
+/** When MEM_COPY is true T will be bit copied when moved.
+    When MEM_COPY is false, T will be copy constructed / destructed.
+    In all cases T's constructor will be called on allocation,
+    and its destructor will be called from this object's destructor.
+*/
+template <typename T, bool MEM_COPY> class SkTArray {
+public:
+    /**
+     * Creates an empty array with no initial storage
+     */
+    SkTArray() {
+        fCount = 0;
+        fReserveCount = gMIN_ALLOC_COUNT;
+        fAllocCount = 0;
+        fMemArray = NULL;
+        fPreAllocMemArray = NULL;
+    }
+
+    /**
+     * 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.
+     */
+    explicit SkTArray(const SkTArray& array) {
+        this->init(array.fItemArray, array.fCount, NULL, 0);
+    }
+
+    /**
+     * 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.
+     */
+    SkTArray(const T* array, int count) {
+        this->init(array, count, NULL, 0);
+    }
+
+    /**
+     * assign copy of array to this
+     */
+    SkTArray& operator =(const SkTArray& array) {
+        for (int i = 0; i < fCount; ++i) {
+            fItemArray[i].~T();
+        }
+        fCount = 0;
+        checkRealloc((int)array.count());
+        fCount = array.count();
+        SkTArrayExt::copy(this, static_cast<const T*>(array.fMemArray));
+        return *this;
+    }
+
+    virtual ~SkTArray() {
+        for (int i = 0; i < fCount; ++i) {
+            fItemArray[i].~T();
+        }
+        if (fMemArray != fPreAllocMemArray) {
+            sk_free(fMemArray);
+        }
+    }
+
+    /**
+     * Resets to count() == 0
+     */
+    void reset() { this->pop_back_n(fCount); }
+
+    /**
+     * Number of elements in the array.
+     */
+    int count() const { return fCount; }
+
+    /**
+     * Is the array empty.
+     */
+    bool empty() const { return !fCount; }
+
+    /**
+     * Adds 1 new default-constructed T value and returns in by reference. Note
+     * the reference only remains valid until the next call that adds or removes
+     * elements.
+     */
+    T& push_back() {
+        checkRealloc(1);
+        new ((char*)fMemArray+sizeof(T)*fCount) T;
+        ++fCount;
+        return fItemArray[fCount-1];
+    }
+
+    /**
+     * Version of above that uses a copy constructor to initialize the new item
+     */
+    T& push_back(const T& t) {
+        checkRealloc(1);
+        new ((char*)fMemArray+sizeof(T)*fCount) T(t);
+        ++fCount;
+        return fItemArray[fCount-1];
+    }
+
+    /**
+     * Allocates n more default T values, and returns the address of the start
+     * of that new range. Note: this address is only valid until the next API
+     * call made on the array that might add or remove elements.
+     */
+    T* push_back_n(int n) {
+        SkASSERT(n >= 0);
+        checkRealloc(n);
+        for (int i = 0; i < n; ++i) {
+            new (fItemArray + fCount + i) T;
+        }
+        fCount += n;
+        return fItemArray + fCount - n;
+    }
+
+    /**
+     * Version of above that uses a copy constructor to initialize all n items
+     * to the same T.
+     */
+    T* push_back_n(int n, const T& t) {
+        SkASSERT(n >= 0);
+        checkRealloc(n);
+        for (int i = 0; i < n; ++i) {
+            new (fItemArray + fCount + i) T(t);
+        }
+        fCount += n;
+        return fItemArray + fCount - n;
+    }
+
+    /**
+     * Version of above that uses a copy constructor to initialize the n items
+     * to separate T values.
+     */
+    T* push_back_n(int n, const T t[]) {
+        SkASSERT(n >= 0);
+        checkRealloc(n);
+        for (int i = 0; i < n; ++i) {
+            new (fItemArray + fCount + i) T(t[i]);
+        }
+        fCount += n;
+        return fItemArray + fCount - n;
+    }
+
+    /**
+     * Removes the last element. Not safe to call when count() == 0.
+     */
+    void pop_back() {
+        SkASSERT(fCount > 0);
+        --fCount;
+        fItemArray[fCount].~T();
+        checkRealloc(0);
+    }
+
+    /**
+     * Removes the last n elements. Not safe to call when count() < n.
+     */
+    void pop_back_n(int n) {
+        SkASSERT(n >= 0);
+        SkASSERT(fCount >= n);
+        fCount -= n;
+        for (int i = 0; i < n; ++i) {
+            fItemArray[i].~T();
+        }
+        checkRealloc(0);
+    }
+
+    /**
+     * Pushes or pops from the back to resize. Pushes will be default
+     * initialized.
+     */
+    void resize_back(int newCount) {
+        SkASSERT(newCount >= 0);
+
+        if (newCount > fCount) {
+            push_back_n(newCount - fCount);
+        } else if (newCount < fCount) {
+            pop_back_n(fCount - newCount);
+        }
+    }
+
+    /**
+     * Get the i^th element.
+     */
+    T& operator[] (int i) {
+        SkASSERT(i < fCount);
+        SkASSERT(i >= 0);
+        return fItemArray[i];
+    }
+
+    const T& operator[] (int i) const {
+        SkASSERT(i < fCount);
+        SkASSERT(i >= 0);
+        return fItemArray[i];
+    }
+
+    /**
+     * equivalent to operator[](0)
+     */
+    T& front() { SkASSERT(fCount > 0); return fItemArray[0];}
+
+    const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];}
+
+    /**
+     * equivalent to operator[](count() - 1)
+     */
+    T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];}
+
+    const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];}
+
+    /**
+     * equivalent to operator[](count()-1-i)
+     */
+    T& fromBack(int i) {
+        SkASSERT(i >= 0);
+        SkASSERT(i < fCount);
+        return fItemArray[fCount - i - 1];
+    }
+
+    const T& fromBack(int i) const {
+        SkASSERT(i >= 0);
+        SkASSERT(i < fCount);
+        return fItemArray[fCount - i - 1];
+    }
+
+protected:
+    /**
+     * Creates an empty array that will use the passed storage block until it
+     * is insufficiently large to hold the entire array.
+     */
+    template <int N>
+    SkTArray(SkAlignedSTStorage<N,T>* storage) {
+        this->init(NULL, 0, storage->get(), N);
+    }
+
+    /**
+     * Copy another array, using preallocated storage if preAllocCount >=
+     * array.count(). Otherwise storage will only be used when array shrinks
+     * to fit.
+     */
+    template <int N>
+    SkTArray(const SkTArray& array, SkAlignedSTStorage<N,T>* storage) {
+        this->init(array.fItemArray, array.fCount, storage->get(), N);
+    }
+
+    /**
+     * Copy a C array, using preallocated storage if preAllocCount >=
+     * count. Otherwise storage will only be used when array shrinks
+     * to fit.
+     */
+    template <int N>
+    SkTArray(const T* array, int count, SkAlignedSTStorage<N,T>* storage) {
+        this->init(array, count, storage->get(), N);
+    }
+
+    void init(const T* array, int count,
+               void* preAllocStorage, int preAllocOrReserveCount) {
+        SkASSERT(count >= 0);
+        SkASSERT(preAllocOrReserveCount >= 0);
+        fCount              = count;
+        fReserveCount       = (preAllocOrReserveCount > 0) ?
+                                    preAllocOrReserveCount :
+                                    gMIN_ALLOC_COUNT;
+        fPreAllocMemArray   = preAllocStorage;
+        if (fReserveCount >= fCount &&
+            NULL != preAllocStorage) {
+            fAllocCount = fReserveCount;
+            fMemArray = preAllocStorage;
+        } else {
+            fAllocCount = SkMax32(fCount, fReserveCount);
+            fMemArray = sk_malloc_throw(fAllocCount * sizeof(T));
+        }
+
+        SkTArrayExt::copy(this, array);
+    }
+
+private:
+
+    static const int gMIN_ALLOC_COUNT = 8;
+
+    inline void checkRealloc(int delta) {
+        SkASSERT(fCount >= 0);
+        SkASSERT(fAllocCount >= 0);
+
+        SkASSERT(-delta <= fCount);
+
+        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 (newAllocCount != fAllocCount) {
+
+            fAllocCount = newAllocCount;
+            char* newMemArray;
+
+            if (fAllocCount == fReserveCount && NULL != fPreAllocMemArray) {
+                newMemArray = (char*) fPreAllocMemArray;
+            } else {
+                newMemArray = (char*) sk_malloc_throw(fAllocCount*sizeof(T));
+            }
+
+            SkTArrayExt::copyAndDelete<T>(this, newMemArray);
+
+            if (fMemArray != fPreAllocMemArray) {
+                sk_free(fMemArray);
+            }
+            fMemArray = newMemArray;
+        }
+    }
+
+    template<typename X> friend void SkTArrayExt::copy(SkTArray<X, true>* that, const X*);
+    template<typename X> friend void SkTArrayExt::copyAndDelete(SkTArray<X, true>* that, char*);
+
+    template<typename X> friend void SkTArrayExt::copy(SkTArray<X, false>* that, const X*);
+    template<typename X> friend void SkTArrayExt::copyAndDelete(SkTArray<X, false>* that, char*);
+
+    int fReserveCount;
+    int fCount;
+    int fAllocCount;
+    void*    fPreAllocMemArray;
+    union {
+        T*       fItemArray;
+        void*    fMemArray;
+    };
+};
+
+/**
+ * Subclass of SkTArray that contains a preallocated memory block for the array.
+ */
+template <int N, typename T, bool DATA_TYPE = false>
+class SkSTArray : public SkTArray<T, DATA_TYPE> {
+private:
+    typedef SkTArray<T, DATA_TYPE> INHERITED;
+
+public:
+    SkSTArray() : INHERITED(&fStorage) {
+    }
+
+    SkSTArray(const SkSTArray& array)
+        : INHERITED(array, &fStorage) {
+    }
+
+    explicit SkSTArray(const INHERITED& array)
+        : INHERITED(array, &fStorage) {
+    }
+
+    SkSTArray(const T* array, int count)
+        : INHERITED(array, count, &fStorage) {
+    }
+
+    SkSTArray& operator= (const SkSTArray& array) {
+        return *this = *(const INHERITED*)&array;
+    }
+
+    SkSTArray& operator= (const INHERITED& array) {
+        INHERITED::operator=(array);
+        return *this;
+    }
+
+private:
+    SkAlignedSTStorage<N,T> fStorage;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkTDArray.h b/legacy/include/core/SkTDArray.h
new file mode 100644
index 0000000..5f62203
--- /dev/null
+++ b/legacy/include/core/SkTDArray.h
@@ -0,0 +1,317 @@
+
+/*
+ * 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 SkTDArray_DEFINED
+#define SkTDArray_DEFINED
+
+#include "SkTypes.h"
+
+template <typename T> class SK_API SkTDArray {
+public:
+    SkTDArray() {
+        fReserve = fCount = 0;
+        fArray = NULL;
+#ifdef SK_DEBUG
+        fData = NULL;
+#endif
+    }
+    SkTDArray(const T src[], size_t count) {
+        SkASSERT(src || count == 0);
+
+        fReserve = fCount = 0;
+        fArray = NULL;
+#ifdef SK_DEBUG
+        fData = NULL;
+#endif
+        if (count) {
+            fArray = (T*)sk_malloc_throw(count * sizeof(T));
+#ifdef SK_DEBUG
+            fData = (ArrayT*)fArray;
+#endif
+            memcpy(fArray, src, sizeof(T) * count);
+            fReserve = fCount = count;
+        }
+    }
+    SkTDArray(const SkTDArray<T>& src) {
+        fReserve = fCount = 0;
+        fArray = NULL;
+#ifdef SK_DEBUG
+        fData = NULL;
+#endif
+        SkTDArray<T> tmp(src.fArray, src.fCount);
+        this->swap(tmp);
+    }
+    ~SkTDArray() {
+        sk_free(fArray);
+    }
+
+    SkTDArray<T>& operator=(const SkTDArray<T>& src) {
+        if (this != &src) {
+            if (src.fCount > fReserve) {
+                SkTDArray<T> tmp(src.fArray, src.fCount);
+                this->swap(tmp);
+            } else {
+                memcpy(fArray, src.fArray, sizeof(T) * src.fCount);
+                fCount = src.fCount;
+            }
+        }
+        return *this;
+    }
+
+    friend bool operator==(const SkTDArray<T>& a, const SkTDArray<T>& b) {
+        return  a.fCount == b.fCount &&
+                (a.fCount == 0 ||
+                 !memcmp(a.fArray, b.fArray, a.fCount * sizeof(T)));
+    }
+
+    void swap(SkTDArray<T>& other) {
+        SkTSwap(fArray, other.fArray);
+#ifdef SK_DEBUG
+        SkTSwap(fData, other.fData);
+#endif
+        SkTSwap(fReserve, other.fReserve);
+        SkTSwap(fCount, other.fCount);
+    }
+
+    /** Return a ptr to the array of data, to be freed with sk_free. This also
+        resets the SkTDArray to be empty.
+     */
+    T* detach() {
+        T* array = fArray;
+        fArray = NULL;
+        fReserve = fCount = 0;
+        SkDEBUGCODE(fData = NULL;)
+        return array;
+    }
+
+    bool isEmpty() const { return fCount == 0; }
+
+    /**
+     *  Return the number of elements in the array
+     */
+    int count() const { return fCount; }
+
+    /**
+     *  return the number of bytes in the array: count * sizeof(T)
+     */
+    size_t bytes() const { return fCount * sizeof(T); }
+
+    T*  begin() const { return fArray; }
+    T*  end() const { return fArray ? fArray + fCount : NULL; }
+    T&  operator[](int index) const {
+        SkASSERT((unsigned)index < fCount);
+        return fArray[index];
+    }
+
+    void reset() {
+        if (fArray) {
+            sk_free(fArray);
+            fArray = NULL;
+#ifdef SK_DEBUG
+            fData = NULL;
+#endif
+            fReserve = fCount = 0;
+        } else {
+            SkASSERT(fReserve == 0 && fCount == 0);
+        }
+    }
+    
+    void rewind() {
+        // same as setCount(0)
+        fCount = 0;
+    }
+
+    void setCount(size_t count) {
+        if (count > fReserve) {
+            this->growBy(count - fCount);
+        } else {
+            fCount = count;
+        }
+    }
+
+    void setReserve(size_t reserve) {
+        if (reserve > fReserve) {
+            SkASSERT(reserve > fCount);
+            size_t count = fCount;
+            this->growBy(reserve - fCount);
+            fCount = count;
+        }
+    }
+
+    T* prepend() {
+        this->growBy(1);
+        memmove(fArray + 1, fArray, (fCount - 1) * sizeof(T));
+        return fArray;
+    }
+
+    T* append() {
+        return this->append(1, NULL);
+    }
+    T* append(size_t count, const T* src = NULL) {
+        unsigned oldCount = fCount;
+        if (count)  {
+            SkASSERT(src == NULL || fArray == NULL ||
+                    src + count <= fArray || fArray + oldCount <= src);
+
+            this->growBy(count);
+            if (src) {
+                memcpy(fArray + oldCount, src, sizeof(T) * count);
+            }
+        }
+        return fArray + oldCount;
+    }
+    
+    T* appendClear() {
+        T* result = this->append(); 
+        *result = 0;
+        return result;
+    }
+
+    T* insert(size_t index) {
+        return this->insert(index, 1, NULL);
+    }
+    T* insert(size_t index, size_t count, const T* src = NULL) {
+        SkASSERT(count);
+        SkASSERT(index <= fCount);
+        int oldCount = fCount;
+        this->growBy(count);
+        T* dst = fArray + index;
+        memmove(dst + count, dst, sizeof(T) * (oldCount - index));
+        if (src) {
+            memcpy(dst, src, sizeof(T) * count);
+        }
+        return dst;
+    }
+
+    void remove(size_t index, size_t count = 1) {
+        SkASSERT(index + count <= fCount);
+        fCount = fCount - count;
+        memmove(fArray + index, fArray + index + count, sizeof(T) * (fCount - index));
+    }
+
+    void removeShuffle(size_t index) {
+        SkASSERT(index < fCount);
+        unsigned newCount = fCount - 1;
+        fCount = newCount;
+        if (index != newCount) {
+            memcpy(fArray + index, fArray + newCount, sizeof(T));
+        }
+    }
+
+    int find(const T& elem) const {
+        const T* iter = fArray;
+        const T* stop = fArray + fCount;
+
+        for (; iter < stop; iter++) {
+            if (*iter == elem) {
+                return (int) (iter - fArray);
+            }
+        }
+        return -1;
+    }
+
+    int rfind(const T& elem) const {
+        const T* iter = fArray + fCount;
+        const T* stop = fArray;
+
+        while (iter > stop) {
+            if (*--iter == elem) {
+                return iter - stop;
+            }
+        }
+        return -1;
+    }
+
+    // routines to treat the array like a stack
+    T*          push() { return this->append(); }
+    void        push(const T& elem) { *this->append() = elem; }
+    const T&    top() const { return (*this)[fCount - 1]; }
+    T&          top() { return (*this)[fCount - 1]; }
+    void        pop(T* elem) { if (elem) *elem = (*this)[fCount - 1]; --fCount; }
+    void        pop() { --fCount; }
+
+    void deleteAll() {
+        T*  iter = fArray;
+        T*  stop = fArray + fCount;
+        while (iter < stop) {
+            delete (*iter);
+            iter += 1;
+        }
+        this->reset();
+    }
+
+    void freeAll() {
+        T*  iter = fArray;
+        T*  stop = fArray + fCount;
+        while (iter < stop) {
+            sk_free(*iter);
+            iter += 1;
+        }
+        this->reset();
+    }
+
+    void unrefAll() {
+        T*  iter = fArray;
+        T*  stop = fArray + fCount;
+        while (iter < stop) {
+            (*iter)->unref();
+            iter += 1;
+        }
+        this->reset();
+    }
+
+    void safeUnrefAll() {
+        T*  iter = fArray;
+        T*  stop = fArray + fCount;
+        while (iter < stop) {
+            SkSafeUnref(*iter);
+            iter += 1;
+        }
+        this->reset();
+    }
+
+#ifdef SK_DEBUG
+    void validate() const {
+        SkASSERT((fReserve == 0 && fArray == NULL) ||
+                 (fReserve > 0 && fArray != NULL));
+        SkASSERT(fCount <= fReserve);
+        SkASSERT(fData == (ArrayT*)fArray);
+    }
+#endif
+
+private:
+#ifdef SK_DEBUG
+    enum {
+        kDebugArraySize = 16
+    };
+    typedef T ArrayT[kDebugArraySize];
+    ArrayT* fData;
+#endif
+    T*      fArray;
+    size_t  fReserve, fCount;
+
+    void growBy(size_t extra) {
+        SkASSERT(extra);
+
+        if (fCount + extra > fReserve) {
+            size_t size = fCount + extra + 4;
+            size += size >> 2;
+
+            fArray = (T*)sk_realloc_throw(fArray, size * sizeof(T));
+#ifdef SK_DEBUG
+            fData = (ArrayT*)fArray;
+#endif
+            fReserve = size;
+        }
+        fCount += extra;
+    }
+};
+
+#endif
+
diff --git a/legacy/include/core/SkTDStack.h b/legacy/include/core/SkTDStack.h
new file mode 100644
index 0000000..be34e01
--- /dev/null
+++ b/legacy/include/core/SkTDStack.h
@@ -0,0 +1,111 @@
+
+/*
+ * 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 SkTDStack_DEFINED
+#define SkTDStack_DEFINED
+
+#include "SkTypes.h"
+
+template <typename T> class SkTDStack : SkNoncopyable {
+public:
+    SkTDStack() : fCount(0), fTotalCount(0) {
+        fInitialRec.fNext = NULL;
+        fRec = &fInitialRec;
+
+    //  fCount = kSlotCount;
+    }
+
+    ~SkTDStack() {
+        Rec* rec = fRec;
+        while (rec != &fInitialRec) {
+            Rec* next = rec->fNext;
+            sk_free(rec);
+            rec = next;
+        }
+    }
+
+    int count() const { return fTotalCount; }
+    int depth() const { return fTotalCount; }
+    bool empty() const { return fTotalCount == 0; }
+
+    T* push() {
+        SkASSERT(fCount <= kSlotCount);
+        if (fCount == kSlotCount) {
+            Rec* rec = (Rec*)sk_malloc_throw(sizeof(Rec));
+            rec->fNext = fRec;
+            fRec = rec;
+            fCount = 0;
+        }
+        ++fTotalCount;
+        return &fRec->fSlots[fCount++];
+    }
+
+    void push(const T& elem) { *this->push() = elem; }
+
+    const T& index(int idx) const {
+        SkASSERT(fRec && fCount > idx);
+        return fRec->fSlots[fCount - idx - 1];
+    }
+
+    T& index(int idx) {
+        SkASSERT(fRec && fCount > idx);
+        return fRec->fSlots[fCount - idx - 1];
+    }
+
+    const T& top() const {
+        SkASSERT(fRec && fCount > 0);
+        return fRec->fSlots[fCount - 1];
+    }
+
+    T& top() {
+        SkASSERT(fRec && fCount > 0);
+        return fRec->fSlots[fCount - 1];
+    }
+
+    void pop(T* elem) {
+        if (elem) {
+            *elem = fRec->fSlots[fCount - 1];
+        }
+        this->pop();
+    }
+
+    void pop() {
+        SkASSERT(fCount > 0 && fRec);
+        --fTotalCount;
+        if (--fCount == 0) {
+            if (fRec != &fInitialRec) {
+                Rec* rec = fRec->fNext;
+                sk_free(fRec);
+                fCount = kSlotCount;
+                fRec = rec;
+            } else {
+                SkASSERT(fTotalCount == 0);
+            }
+        }
+    }
+
+private:
+    enum {
+        kSlotCount  = 8
+    };
+
+    struct Rec;
+    friend struct Rec;
+
+    struct Rec {
+        Rec* fNext;
+        T    fSlots[kSlotCount];
+    };
+    Rec     fInitialRec;
+    Rec*    fRec;
+    int     fCount, fTotalCount;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkTDict.h b/legacy/include/core/SkTDict.h
new file mode 100644
index 0000000..3620899
--- /dev/null
+++ b/legacy/include/core/SkTDict.h
@@ -0,0 +1,162 @@
+
+/*
+ * 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 SkTDict_DEFINED
+#define SkTDict_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkTSearch.h"
+#include "SkTDArray.h"
+
+template <typename T> class SkTDict : SkNoncopyable {
+public:
+    SkTDict(size_t minStringAlloc) : fStrings(minStringAlloc) {}
+
+    void reset()
+    {
+        fArray.reset();
+        fStrings.reset();
+    }
+
+    int count() const { return fArray.count(); }
+
+    bool set(const char name[], const T& value)
+    {
+        return set(name, strlen(name), value);
+    }
+
+    bool set(const char name[], size_t len, const T& value)
+    {
+        SkASSERT(name);
+
+        int index = this->find_index(name, len);
+
+        if (index >= 0)
+        {
+            fArray[index].fValue = value;
+            return false;
+        }
+        else
+        {
+            Pair*   pair = fArray.insert(~index);
+            char*   copy = (char*)fStrings.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+            memcpy(copy, name, len);
+            copy[len] = '\0';
+            pair->fName = copy;
+            pair->fValue = value;
+            return true;
+        }
+    }
+
+    bool find(const char name[]) const
+    {
+        return this->find_index(name) >= 0;
+    }
+
+    bool find(const char name[], size_t len) const
+    {
+        return this->find_index(name, len) >= 0;
+    }
+
+    bool find(const char name[], T* value) const
+    {
+        return find(name, strlen(name), value);
+    }
+
+    bool find(const char name[], size_t len, T* value) const
+    {
+        int index = this->find_index(name, len);
+
+        if (index >= 0)
+        {
+            if (value)
+                *value = fArray[index].fValue;
+            return true;
+        }
+        return false;
+    }
+
+    bool findKey(T& value, const char** name) const
+    {
+        Pair* end = fArray.end();
+        for (Pair* pair = fArray.begin(); pair < end; pair++) {
+            if (pair->fValue != value)
+                continue;
+            *name = pair->fName;
+            return true;
+        }
+        return false;
+    }
+
+public:
+    struct Pair {
+        const char* fName;
+        T           fValue;
+
+        friend int operator<(const Pair& a, const Pair& b)
+        {
+            return strcmp(a.fName, b.fName);
+        }
+        friend int operator!=(const Pair& a, const Pair& b)
+        {
+            return strcmp(a.fName, b.fName);
+        }
+    };
+    friend class Iter;
+
+public:
+    class Iter {
+    public:
+        Iter(const SkTDict<T>& dict)
+        {
+            fIter = dict.fArray.begin();
+            fStop = dict.fArray.end();
+        }
+        const char* next(T* value)
+        {
+            const char* name = NULL;
+            if (fIter < fStop)
+            {
+                name = fIter->fName;
+                if (value)
+                    *value = fIter->fValue;
+                fIter += 1;
+            }
+            return name;
+        }
+    private:
+        Pair*   fIter;
+        Pair*   fStop;
+    };
+
+private:
+    SkTDArray<Pair> fArray;
+    SkChunkAlloc    fStrings;
+
+    int find_index(const char name[]) const
+    {
+        return find_index(name, strlen(name));
+    }
+
+    int find_index(const char name[], size_t len) const
+    {
+        SkASSERT(name);
+
+        int count = fArray.count();
+        int index = ~0;
+
+        if (count)
+            index = SkStrSearch(&fArray.begin()->fName, count, name, len, sizeof(Pair));
+        return index;
+    }
+    friend class Iter;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkTLazy.h b/legacy/include/core/SkTLazy.h
new file mode 100644
index 0000000..9cfaccb
--- /dev/null
+++ b/legacy/include/core/SkTLazy.h
@@ -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.
+ */
+
+
+
+#ifndef SkTLazy_DEFINED
+#define SkTLazy_DEFINED
+
+#include "SkTypes.h"
+#include <new>
+
+/**
+ *  Efficient way to defer allocating/initializing a class until it is needed
+ *  (if ever).
+ */
+template <typename T> class SkTLazy {
+public:
+    SkTLazy() : fPtr(NULL) {}
+
+    explicit SkTLazy(const T* src) : fPtr(NULL) {
+        if (src) {
+            fPtr = new (fStorage) T(*src);
+        }
+    }
+
+    SkTLazy(const SkTLazy<T>& src) : fPtr(NULL) {
+        if (src.isValid()) {
+            fPtr = new (fStorage) T(*src->get());
+        } else {
+            fPtr = NULL;
+        }
+    }
+
+    ~SkTLazy() {
+        if (this->isValid()) {
+            fPtr->~T();
+        }
+    }
+
+    /**
+     *  Return a pointer to a default-initialized instance of the class. If a
+     *  previous instance had been initialzied (either from init() or set()) it
+     *  will first be destroyed, so that a freshly initialized instance is
+     *  always returned.
+     */
+    T* init() {
+        if (this->isValid()) {
+            fPtr->~T();
+        }
+        fPtr = new (SkTCast<T*>(fStorage)) T;
+        return fPtr;
+    }
+
+    /**
+     *  Copy src into this, and return a pointer to a copy of it. Note this
+     *  will always return the same pointer, so if it is called on a lazy that
+     *  has already been initialized, then this will copy over the previous
+     *  contents.
+     */
+    T* set(const T& src) {
+        if (this->isValid()) {
+            *fPtr = src;
+        } else {
+            fPtr = new (SkTCast<T*>(fStorage)) T(src);
+        }
+        return fPtr;
+    }
+
+    /**
+     *  Returns true if a valid object has been initialized in the SkTLazy,
+     *  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
+
diff --git a/legacy/include/core/SkTRegistry.h b/legacy/include/core/SkTRegistry.h
new file mode 100644
index 0000000..34fcffd
--- /dev/null
+++ b/legacy/include/core/SkTRegistry.h
@@ -0,0 +1,56 @@
+
+/*
+ * 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 SkTRegistry_DEFINED
+#define SkTRegistry_DEFINED
+
+#include "SkTypes.h"
+
+/** Template class that registers itself (in the constructor) into a linked-list
+    and provides a function-pointer. This can be used to auto-register a set of
+    services, e.g. a set of image codecs.
+ */
+template <typename T, typename P> class SkTRegistry : SkNoncopyable {
+public:
+    typedef T (*Factory)(P);
+
+    SkTRegistry(Factory fact) {
+#ifdef SK_BUILD_FOR_ANDROID
+        // work-around for double-initialization bug
+        {
+            SkTRegistry* reg = gHead;
+            while (reg) {
+                if (reg == this) {
+                    return;
+                }
+                reg = reg->fChain;
+            }
+        }
+#endif
+        fFact = fact;
+        fChain = gHead;
+        gHead = this;
+    }
+
+    static const SkTRegistry* Head() { return gHead; }
+
+    const SkTRegistry* next() const { return fChain; }
+    Factory factory() const { return fFact; }
+
+private:
+    Factory      fFact;
+    SkTRegistry* fChain;
+
+    static SkTRegistry* gHead;
+};
+
+// The caller still needs to declare an instance of this somewhere
+template <typename T, typename P> SkTRegistry<T, P>* SkTRegistry<T, P>::gHead;
+
+#endif
diff --git a/legacy/include/core/SkTScopedPtr.h b/legacy/include/core/SkTScopedPtr.h
new file mode 100644
index 0000000..580d72f
--- /dev/null
+++ b/legacy/include/core/SkTScopedPtr.h
@@ -0,0 +1,76 @@
+
+/*
+ * 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 SkTScopedPtr_DEFINED
+#define SkTScopedPtr_DEFINED
+
+#include "SkTypes.h"
+
+/** \class SkTScopedPtr
+  A SkTScopedPtr<T> is like a T*, except that the destructor of SkTScopedPtr<T>
+  automatically deletes the pointer it holds (if any).  That is, SkTScopedPtr<T>
+  owns the T object that it points to.  Like a T*, a SkTScopedPtr<T> may hold
+  either NULL or a pointer to a T object.  Also like T*, SkTScopedPtr<T> is
+  thread-compatible, and once you dereference it, you get the threadsafety
+  guarantees of T.
+
+  The size of a SkTScopedPtr is small: sizeof(SkTScopedPtr<T>) == sizeof(T*)
+*/
+template <typename T> class SkTScopedPtr : SkNoncopyable {
+public:
+    explicit SkTScopedPtr(T* o = NULL) : fObj(o) {}
+    ~SkTScopedPtr() {
+        enum { kTypeMustBeComplete = sizeof(T) };
+        delete fObj;
+    }
+
+    /** Delete the current object, if any.  Then take ownership of the
+        passed object.
+     */
+    void reset(T* o = NULL) {
+        if (o != fObj) {
+            enum { kTypeMustBeComplete = sizeof(T) };
+            delete fObj;
+            fObj = o;
+        }
+    }
+
+    /** Without deleting the current object, return it and forget about it.
+        Similar to calling get() and reset(), but the object is not deleted.
+     */
+    T* release() {
+        T* retVal = fObj;
+        fObj = NULL;
+        return retVal;
+    }
+
+    T& operator*() const {
+        SkASSERT(fObj != NULL);
+        return *fObj;
+    }
+    T* operator->() const  {
+        SkASSERT(fObj != NULL);
+        return fObj;
+    }
+    T* get() const { return fObj; }
+
+    bool operator==(T* o) const { return fObj == o; }
+    bool operator!=(T* o) const { return fObj != o; }
+
+private:
+    T* fObj;
+
+    // Forbid comparison of SkTScopedPtr types.  If T2 != T, it doesn't make
+    // sense, and if T2 == T, it still doesn't make sense because the same
+    // object can't be owned by two different scoped_ptrs.
+    template <class T2> bool operator==(SkTScopedPtr<T2> const& o2) const;
+    template <class T2> bool operator!=(SkTScopedPtr<T2> const& o2) const;
+};
+
+#endif
diff --git a/legacy/include/core/SkTSearch.h b/legacy/include/core/SkTSearch.h
new file mode 100644
index 0000000..d5ab18c
--- /dev/null
+++ b/legacy/include/core/SkTSearch.h
@@ -0,0 +1,158 @@
+
+/*
+ * 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 SkTSearch_DEFINED
+#define SkTSearch_DEFINED
+
+#include "SkTypes.h"
+
+template <typename 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 (*elem < target)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+
+    const T* elem = (const T*)((const char*)base + hi * elemSize);
+    if (*elem != target)
+    {
+        if (*elem < target)
+            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;
+        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;
+        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[],
+                size_t elemSize);
+
+/** Like SkStrSearch, but treats target as if it were all lower-case. Assumes that
+    base points to a table of lower-case strings.
+*/
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t target_len, size_t elemSize);
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t elemSize);
+
+/** Helper class to convert a string to lower-case, but only modifying the ascii
+    characters. This makes the routine very fast and never changes the string
+    length, but it is not suitable for linguistic purposes. Normally this is
+    used for buiding and searching string tables.
+*/
+class SkAutoAsciiToLC {
+public:
+    SkAutoAsciiToLC(const char str[], size_t len = (size_t)-1);
+    ~SkAutoAsciiToLC();
+    
+    const char* lc() const { return fLC; }
+    size_t      length() const { return fLength; }
+
+private:
+    char*   fLC;    // points to either the heap or fStorage
+    size_t  fLength;
+    enum {
+        STORAGE = 64
+    };
+    char    fStorage[STORAGE+1];
+};
+
+extern "C" {
+    typedef int (*SkQSortCompareProc)(const void*, const void*);
+    void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc);
+}
+
+#endif
+
diff --git a/legacy/include/core/SkTemplates.h b/legacy/include/core/SkTemplates.h
new file mode 100644
index 0000000..03f0892
--- /dev/null
+++ b/legacy/include/core/SkTemplates.h
@@ -0,0 +1,298 @@
+
+/*
+ * 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 SkTemplates_DEFINED
+#define SkTemplates_DEFINED
+
+#include "SkTypes.h"
+
+/** \file SkTemplates.h
+
+    This file contains light-weight template classes for type-safe and exception-safe
+    resource management.
+*/
+
+/** \class SkAutoTCallVProc
+
+    Call a function when this goes out of scope. The template uses two
+    parameters, the object, and a function that is to be called in the destructor.
+    If detach() is called, the object reference is set to null. If the object
+    reference is null when the destructor is called, we do not call the
+    function.
+*/
+template <typename T, void (*P)(T*)> class SkAutoTCallVProc : SkNoncopyable {
+public:
+    SkAutoTCallVProc(T* obj): fObj(obj) {}
+    ~SkAutoTCallVProc() { if (fObj) P(fObj); }
+    T* detach() { T* obj = fObj; fObj = NULL; return obj; }
+private:
+    T* fObj;
+};
+
+/** \class SkAutoTCallIProc
+
+Call a function when this goes out of scope. The template uses two
+parameters, the object, and a function that is to be called in the destructor.
+If detach() is called, the object reference is set to null. If the object
+reference is null when the destructor is called, we do not call the
+function.
+*/
+template <typename T, int (*P)(T*)> class SkAutoTCallIProc : SkNoncopyable {
+public:
+    SkAutoTCallIProc(T* obj): fObj(obj) {}
+    ~SkAutoTCallIProc() { if (fObj) P(fObj); }
+    T* detach() { T* obj = fObj; fObj = NULL; return obj; }
+private:
+    T* fObj;
+};
+
+// 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; }
+
+    T*      get() const { return fObj; }
+    void    free() { delete fObj; fObj = NULL; }
+    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; }
+
+    T*      get() const { return fArray; }
+    void    free() { delete[] fArray; fArray = NULL; }
+    T*      detach() { T* array = fArray; fArray = NULL; return array; }
+
+private:
+    T*  fArray;
+};
+
+/** Allocate an array of T elements, and free the array in the destructor
+ */
+template <typename T> class SkAutoTArray : SkNoncopyable {
+public:
+    /** Allocate count number of T elements
+     */
+    SkAutoTArray(size_t count) {
+        fArray = NULL;
+        if (count) {
+            fArray = new T[count];
+        }
+        SkDEBUGCODE(fCount = count;)
+    }
+
+    ~SkAutoTArray() {
+        delete[] fArray;
+    }
+
+    /** 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:
+    T*  fArray;
+    SkDEBUGCODE(size_t fCount;)
+};
+
+/** Wraps SkAutoTArray, with room for up to N elements preallocated
+ */
+template <size_t N, typename T> class SkAutoSTArray : SkNoncopyable {
+public:
+    /** Allocate count number of T elements
+     */
+    SkAutoSTArray(size_t count) {
+        if (count > N) {
+            fArray = new T[count];
+        } else if (count) {
+            fArray = new (fStorage) T[count];
+        } else {
+            fArray = NULL;
+        }
+        fCount = count;
+    }
+    
+    ~SkAutoSTArray() {
+        if (fCount > N) {
+            delete[] fArray;
+        } else {
+            T* start = fArray;
+            T* iter = start + fCount;
+            while (iter > start) {
+                (--iter)->~T();
+            }
+        }
+    }
+    
+    /** 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;
+    // since we come right after fArray, fStorage should be properly aligned
+    char    fStorage[N * sizeof(T)];
+};
+
+/** Allocate a temp array on the stack/heap.
+    Does NOT call any constructors/destructors on T (i.e. T must be POD)
+*/
+template <typename T> class SkAutoTMalloc : SkNoncopyable {
+public:
+    SkAutoTMalloc(size_t count) {
+        fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
+    }
+
+    ~SkAutoTMalloc() {
+        sk_free(fPtr);
+    }
+
+    // doesn't preserve contents
+    void reset (size_t count) {
+        sk_free(fPtr);
+        fPtr = fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
+    }
+
+    T* get() const { return fPtr; }
+
+    operator T*() {
+        return fPtr;
+    }
+
+    operator const T*() const {
+        return fPtr;
+    }
+
+    T& operator[](int index) {
+        return fPtr[index];
+    }
+
+    const T& operator[](int index) const {
+        return fPtr[index];
+    }
+
+private:
+    T*  fPtr;
+};
+
+template <size_t N, typename T> class SK_API SkAutoSTMalloc : SkNoncopyable {
+public:
+    SkAutoSTMalloc(size_t count) {
+        if (count <= N) {
+            fPtr = fTStorage;
+        } else {
+            fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
+        }
+    }
+
+    ~SkAutoSTMalloc() {
+        if (fPtr != fTStorage) {
+            sk_free(fPtr);
+        }
+    }
+
+    // doesn't preserve contents
+    void reset(size_t count) {
+        if (fPtr != fTStorage) {
+            sk_free(fPtr);
+        }
+        if (count <= N) {
+            fPtr = fTStorage;
+        } else {
+            fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
+        }
+    }
+
+    T* get() const { return fPtr; }
+
+    operator T*() {
+        return fPtr;
+    }
+
+    operator const T*() const {
+        return fPtr;
+    }
+
+    T& operator[](int index) {
+        return fPtr[index];
+    }
+
+    const T& operator[](int index) const {
+        return fPtr[index];
+    }
+
+private:
+    T*          fPtr;
+    union {
+        uint32_t    fStorage32[(N*sizeof(T) + 3) >> 2];
+        T           fTStorage[1];   // do NOT want to invoke T::T()
+    };
+};
+
+/**
+ * Reserves memory that is aligned on double and pointer boundaries.
+ * Hopefully this is sufficient for all practical purposes.
+ */
+template <size_t N> class SkAlignedSStorage : SkNoncopyable {
+public:
+    void* get() { return fData; }
+private:
+    union {
+        void*   fPtr;
+        double  fDouble;
+        char    fData[N];
+    };
+};
+
+/**
+ * Reserves memory that is aligned on double and pointer boundaries.
+ * Hopefully this is sufficient for all practical purposes. Otherwise,
+ * we have to do some arcane trickery to determine alignment of non-POD
+ * types. Lifetime of the memory is the lifetime of the object.
+ */
+template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable {
+public:
+    /**
+     * Returns void* because this object does not initialize the
+     * memory. Use placement new for types that require a cons.
+     */
+    void* get() { return fStorage.get(); }
+private:
+    SkAlignedSStorage<sizeof(T)*N> fStorage;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkThread.h b/legacy/include/core/SkThread.h
new file mode 100644
index 0000000..1495a16
--- /dev/null
+++ b/legacy/include/core/SkThread.h
@@ -0,0 +1,61 @@
+
+/*
+ * 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 SkThread_DEFINED
+#define SkThread_DEFINED
+
+#include "SkTypes.h"
+#include "SkThread_platform.h"
+
+/****** SkThread_platform needs to define the following...
+
+int32_t sk_atomic_inc(int32_t*);
+int32_t sk_atomic_dec(int32_t*);
+
+class SkMutex {
+public:
+    SkMutex();
+    ~SkMutex();
+
+    void    acquire();
+    void    release();
+};
+
+****************/
+
+class SkAutoMutexAcquire : SkNoncopyable {
+public:
+    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();
+    }
+    /** If the mutex has not been release, release it now.
+    */
+    void release()
+    {
+        if (fMutex)
+        {
+            fMutex->release();
+            fMutex = NULL;
+        }
+    }
+        
+private:
+    SkBaseMutex* fMutex;
+};
+
+#endif
diff --git a/legacy/include/core/SkThread_platform.h b/legacy/include/core/SkThread_platform.h
new file mode 100644
index 0000000..58311e1
--- /dev/null
+++ b/legacy/include/core/SkThread_platform.h
@@ -0,0 +1,118 @@
+
+/*
+ * 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 SkThread_platform_DEFINED
+#define SkThread_platform_DEFINED
+
+#if defined(SK_BUILD_FOR_ANDROID)
+
+#if defined(SK_BUILD_FOR_ANDROID_NDK)
+
+#include <stdint.h>
+
+/* 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) {
+    return __sync_fetch_and_add(addr, 1);
+}
+
+static __attribute__((always_inline)) int32_t sk_atomic_dec(int32_t *addr) {
+    return __sync_fetch_and_add(addr, -1);
+}
+
+#else // !SK_BUILD_FOR_ANDROID_NDK
+
+/* The platform atomics operations are slightly more efficient than the
+ * GCC built-ins, so use them.
+ */
+#include <utils/Atomic.h>
+
+#define sk_atomic_inc(addr)     android_atomic_inc(addr)
+#define sk_atomic_dec(addr)     android_atomic_dec(addr)
+
+#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.
+*/
+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.
+*/
+SK_API int32_t sk_atomic_dec(int32_t* addr);
+
+#endif // !SK_BUILD_FOR_ANDROID
+
+#ifdef SK_USE_POSIX_THREADS
+
+#include <pthread.h>
+
+// A SkBaseMutex is a POD structure that can be directly initialized
+// at declaration time with SK_DECLARE_STATIC/GLOBAL_MUTEX. This avoids the
+// generation of a static initializer in the final machine code (and
+// a corresponding static finalizer).
+//
+struct SkBaseMutex {
+    void    acquire() { pthread_mutex_lock(&fMutex); }
+    void    release() { pthread_mutex_unlock(&fMutex); }
+    pthread_mutex_t  fMutex;
+};
+
+// Using POD-style initialization prevents the generation of a static initializer
+// and keeps the acquire() implementation small and fast.
+#define SK_DECLARE_STATIC_MUTEX(name)   static SkBaseMutex  name = { PTHREAD_MUTEX_INITIALIZER }
+
+// 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 }
+
+// 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 {
+public:
+    SkMutex();
+    ~SkMutex();
+};
+
+#else // !SK_USE_POSIX_THREADS
+
+// In the generic case, SkBaseMutex and SkMutex are the same thing, and we
+// can't easily get rid of static initializers.
+//
+class SkMutex : SkNoncopyable {
+public:
+    SkMutex();
+    ~SkMutex();
+
+    void    acquire();
+    void    release();
+
+private:
+    bool fIsGlobal;
+    enum {
+        kStorageIntCount = 64
+    };
+    uint32_t    fStorage[kStorageIntCount];
+};
+
+typedef SkMutex SkBaseMutex;
+
+#define SK_DECLARE_STATIC_MUTEX(name)           static SkBaseMutex  name
+#define SK_DECLARE_GLOBAL_MUTEX(name)           SkBaseMutex  name
+#define SK_DECLARE_MUTEX_ARRAY(name, count)     SkBaseMutex name[count]
+
+#endif // !SK_USE_POSIX_THREADS
+
+
+#endif
diff --git a/legacy/include/core/SkTime.h b/legacy/include/core/SkTime.h
new file mode 100644
index 0000000..ede1fd9
--- /dev/null
+++ b/legacy/include/core/SkTime.h
@@ -0,0 +1,65 @@
+
+/*
+ * 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 SkTime_DEFINED
+#define SkTime_DEFINED
+
+#include "SkTypes.h"
+
+/** \class SkTime
+    Platform-implemented utilities to return time of day, and millisecond counter.
+*/
+class SkTime {
+public:
+    struct DateTime {
+        uint16_t fYear;          //!< e.g. 2005
+        uint8_t  fMonth;         //!< 1..12
+        uint8_t  fDayOfWeek;     //!< 0..6, 0==Sunday
+        uint8_t  fDay;           //!< 1..31
+        uint8_t  fHour;          //!< 0..23
+        uint8_t  fMinute;        //!< 0..59
+        uint8_t  fSecond;        //!< 0..59
+    };
+    static void GetDateTime(DateTime*);
+
+    static SkMSec GetMSecs();
+};
+
+#if defined(SK_DEBUG) && defined(SK_BUILD_FOR_WIN32)
+    extern SkMSec gForceTickCount;
+#endif
+
+#define SK_TIME_FACTOR      1
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoTime {
+public:
+    // The label is not deep-copied, so its address must remain valid for the
+    // lifetime of this object
+    SkAutoTime(const char* label = NULL, SkMSec minToDump = 0) : fLabel(label)
+    {
+        fNow = SkTime::GetMSecs();
+        fMinToDump = minToDump;
+    }
+    ~SkAutoTime()
+    {
+        SkMSec dur = SkTime::GetMSecs() - fNow;
+        if (dur >= fMinToDump) {
+            SkDebugf("%s %d\n", fLabel ? fLabel : "", dur);
+        }
+    }
+private:
+    const char* fLabel;
+    SkMSec      fNow;
+    SkMSec      fMinToDump;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkTrace.h b/legacy/include/core/SkTrace.h
new file mode 100644
index 0000000..2d48799
--- /dev/null
+++ b/legacy/include/core/SkTrace.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 SkTrace_DEFINED
+#define SkTrace_DEFINED
+
+#ifdef SK_USER_TRACE_INCLUDE_FILE
+
+/* If your system embeds skia and has complex event logging, in
+   src/config/SkUserConfig.h:
+     - define the three SK_TRACE_EVENT macros to map to your system's
+       equivalents,
+     - define the name of the include file in SK_USER_TRACE_INCLUDE_FILE
+   A trivial example is given in src/utils/SkDebugTrace.h.
+
+   All arguments are const char*. Skia typically passes the name of
+   the object and function (and sometimes region of interest within
+   the function) separated by double colons for 'event'.
+
+   SK_TRACE_EVENT1 and SK_TRACE_EVENT2 take one or two arbitrary
+   name-value pairs that you also want to log. SkStringPrintf() is useful
+   for formatting these values.
+
+   For example:
+    SK_TRACE_EVENT0("GrContext::createAndLockTexture");
+    SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses",
+                    "verts", SkStringPrintf("%i", vert - base).c_str());
+*/
+
+    #include SK_USER_TRACE_INCLUDE_FILE
+
+#else
+
+    #define SK_TRACE_EVENT0(event)
+    #define SK_TRACE_EVENT1(event, name1, value1)
+    #define SK_TRACE_EVENT2(event, name1, value1, name2, value2)
+
+#endif
+
+#endif
+
+
diff --git a/legacy/include/core/SkTypeface.h b/legacy/include/core/SkTypeface.h
new file mode 100644
index 0000000..03ed11c
--- /dev/null
+++ b/legacy/include/core/SkTypeface.h
@@ -0,0 +1,163 @@
+
+/*
+ * 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 SkTypeface_DEFINED
+#define SkTypeface_DEFINED
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkRefCnt.h"
+
+class SkStream;
+class SkAdvancedTypefaceMetrics;
+class SkWStream;
+
+typedef uint32_t SkFontID;
+
+/** \class SkTypeface
+
+    The SkTypeface class specifies the typeface and intrinsic style of a font.
+    This is used in the paint, along with optionally algorithmic settings like
+    textSize, textSkewX, textScaleX, kFakeBoldText_Mask, to specify
+    how text appears when drawn (and measured).
+
+    Typeface objects are immutable, and so they can be shared between threads.
+*/
+class SK_API SkTypeface : public SkRefCnt {
+public:
+    /** Style specifies the intrinsic style attributes of a given typeface
+    */
+    enum Style {
+        kNormal = 0,
+        kBold   = 0x01,
+        kItalic = 0x02,
+
+        // helpers
+        kBoldItalic = 0x03
+    };
+
+    /** Returns the typeface's intrinsic style attributes
+    */
+    Style style() const { return fStyle; }
+
+    /** Returns true if getStyle() has the kBold bit set.
+    */
+    bool isBold() const { return (fStyle & kBold) != 0; }
+
+    /** Returns true if getStyle() has the kItalic bit set.
+    */
+    bool isItalic() const { return (fStyle & kItalic) != 0; }
+
+    /** Returns true if the typeface is fixed-width
+     */
+    bool isFixedWidth() const { return fIsFixedWidth; }
+
+    /** Return a 32bit value for this typeface, unique for the underlying font
+        data. Will never return 0.
+     */
+    SkFontID uniqueID() const { return fUniqueID; }
+
+    /** Return the uniqueID for the specified typeface. If the face is null,
+        resolve it to the default font and return its uniqueID. Will never
+        return 0.
+    */
+    static SkFontID UniqueID(const SkTypeface* face);
+
+    /** Returns true if the two typefaces reference the same underlying font,
+        handling either being null (treating null as the default font)
+     */
+    static bool Equal(const SkTypeface* facea, const SkTypeface* faceb);
+
+    /** Return a new reference to the typeface that most closely matches the
+        requested familyName and style. Pass null as the familyName to return
+        the default font for the requested style. Will never return null
+
+        @param familyName  May be NULL. The name of the font family.
+        @param style       The style (normal, bold, italic) of the typeface.
+        @return reference to the closest-matching typeface. Call must call
+                unref() when they are done.
+    */
+    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.
+        If family is NULL, this selects from the default font's family.
+
+        @param family  May be NULL. The name of the existing type face.
+        @param s       The style (normal, bold, italic) of the type face.
+        @return reference to the closest-matching typeface. Call must call
+                unref() when they are done.
+    */
+    static SkTypeface* CreateFromTypeface(const SkTypeface* family, Style s);
+
+    /** Return a new typeface given a file. If the file does not exist, or is
+        not a valid font file, returns null.
+    */
+    static SkTypeface* CreateFromFile(const char path[]);
+
+    /** Return a new typeface given a stream. If the stream is
+        not a valid font file, returns null. Ownership of the stream is
+        transferred, so the caller must not reference it again.
+    */
+    static SkTypeface* CreateFromStream(SkStream* stream);
+
+    /** Write a unique signature to a stream, sufficient to reconstruct a
+        typeface referencing the same font when Deserialize is called.
+     */
+    void serialize(SkWStream*) const;
+
+    /** Given the data previously written by serialize(), return a new instance
+        to a typeface referring to the same font. If that font is not available,
+        return null. If an instance is returned, the caller is responsible for
+        calling unref() when they are done with it.
+     */
+    static SkTypeface* Deserialize(SkStream*);
+
+    /** Retrieve detailed typeface metrics.  Used by the PDF backend.
+        @param perGlyphInfo Indicate what glyph specific information (advances,
+                            names, etc.) should be populated.
+        @param glyphIDs  For per-glyph info, specify subset of the font by
+                         giving glyph ids.  Each integer represents a glyph
+                         id.  Passing NULL means all glyphs in the font.
+        @param glyphIDsCount Number of elements in subsetGlyphIds. Ignored if
+                             glyphIDs is NULL.
+        @return The returned object has already been referenced.
+     */
+    SkAdvancedTypefaceMetrics* getAdvancedTypefaceMetrics(
+            SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+            const uint32_t* glyphIDs = NULL,
+            uint32_t glyphIDsCount = 0) const;
+
+protected:
+    /** uniqueID must be unique (please!) and non-zero
+    */
+    SkTypeface(Style style, SkFontID uniqueID, bool isFixedWidth = false);
+    virtual ~SkTypeface();
+
+private:
+    SkFontID    fUniqueID;
+    Style       fStyle;
+    bool        fIsFixedWidth;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/legacy/include/core/SkTypes.h b/legacy/include/core/SkTypes.h
new file mode 100644
index 0000000..a584421
--- /dev/null
+++ b/legacy/include/core/SkTypes.h
@@ -0,0 +1,568 @@
+
+/*
+ * 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 SkTypes_DEFINED
+#define SkTypes_DEFINED
+
+#include "SkPreConfig.h"
+#include "SkUserConfig.h"
+#include "SkPostConfig.h"
+
+#ifndef SK_IGNORE_STDINT_DOT_H
+    #include <stdint.h>
+#endif
+
+#include <stdio.h>
+
+/** \file SkTypes.h
+*/
+
+/** See SkGraphics::GetVersion() to retrieve these at runtime
+ */
+#define SKIA_VERSION_MAJOR  1
+#define SKIA_VERSION_MINOR  0
+#define SKIA_VERSION_PATCH  0
+
+/*
+    memory wrappers to be implemented by the porting layer (platform)
+*/
+
+/** Called internally if we run out of memory. The platform implementation must
+    not return, but should either throw an exception or otherwise exit.
+*/
+SK_API extern void sk_out_of_memory(void);
+/** Called internally if we hit an unrecoverable error.
+    The platform implementation must not return, but should either throw
+    an exception or otherwise exit.
+*/
+SK_API extern void sk_throw(void);
+
+enum {
+    SK_MALLOC_TEMP  = 0x01, //!< hint to sk_malloc that the requested memory will be freed in the scope of the stack frame
+    SK_MALLOC_THROW = 0x02  //!< instructs sk_malloc to call sk_throw if the memory cannot be allocated.
+};
+/** Return a block of memory (at least 4-byte aligned) of at least the
+    specified size. If the requested memory cannot be returned, either
+    return null (if SK_MALLOC_TEMP bit is clear) or call sk_throw()
+    (if SK_MALLOC_TEMP bit is set). To free the memory, call sk_free().
+*/
+SK_API extern void* sk_malloc_flags(size_t size, unsigned flags);
+/** Same as sk_malloc(), but hard coded to pass SK_MALLOC_THROW as the flag
+*/
+SK_API extern void* sk_malloc_throw(size_t size);
+/** Same as standard realloc(), but this one never returns null on failure. It will throw
+    an exception if it fails.
+*/
+SK_API extern void* sk_realloc_throw(void* buffer, size_t size);
+/** Free memory returned by sk_malloc(). It is safe to pass null.
+*/
+SK_API extern void sk_free(void*);
+
+// bzero is safer than memset, but we can't rely on it, so... sk_bzero()
+static inline void sk_bzero(void* buffer, size_t size) {
+    memset(buffer, 0, size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_OVERRIDE_GLOBAL_NEW
+#include <new>
+
+inline void* operator new(size_t size) {
+    return sk_malloc_throw(size);
+}
+
+inline void operator delete(void* p) {
+    sk_free(p);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SK_INIT_TO_AVOID_WARNING    = 0
+
+#ifndef SkDebugf
+    void SkDebugf(const char format[], ...);
+#endif
+
+#ifdef SK_DEBUG
+    #define SkASSERT(cond)              SK_DEBUGBREAK(cond)
+    #define SkDEBUGFAIL(message)        SkASSERT(false && message)
+    #define SkDEBUGCODE(code)           code
+    #define SkDECLAREPARAM(type, var)   , type var
+    #define SkPARAM(var)                , var
+//  #define SkDEBUGF(args       )       SkDebugf##args
+    #define SkDEBUGF(args       )       SkDebugf args
+    #define SkAssertResult(cond)        SkASSERT(cond)
+#else
+    #define SkASSERT(cond)
+    #define SkDEBUGFAIL(message)
+    #define SkDEBUGCODE(code)
+    #define SkDEBUGF(args)
+    #define SkDECLAREPARAM(type, var)
+    #define SkPARAM(var)
+
+    // unlike SkASSERT, this guy executes its condition in the non-debug build
+    #define SkAssertResult(cond)        cond
+#endif
+
+namespace {
+
+template <bool>
+struct SkCompileAssert {
+};
+
+}  // namespace
+
+#define SK_COMPILE_ASSERT(expr, msg) \
+    typedef SkCompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+
+///////////////////////////////////////////////////////////////////////
+
+/**
+ *  Fast type for signed 8 bits. Use for parameter passing and local variables,
+ *  not for storage.
+ */
+typedef int S8CPU;
+
+/**
+ *  Fast type for unsigned 8 bits. Use for parameter passing and local
+ *  variables, not for storage
+ */
+typedef unsigned U8CPU;
+
+/**
+ *  Fast type for signed 16 bits. Use for parameter passing and local variables,
+ *  not for storage
+ */
+typedef int S16CPU;
+
+/**
+ *  Fast type for unsigned 16 bits. Use for parameter passing and local
+ *  variables, not for storage
+ */
+typedef unsigned U16CPU;
+
+/**
+ *  Meant to be faster than bool (doesn't promise to be 0 or 1,
+ *  just 0 or non-zero
+ */
+typedef int SkBool;
+
+/**
+ *  Meant to be a small version of bool, for storage purposes. Will be 0 or 1
+ */
+typedef uint8_t SkBool8;
+
+#ifdef SK_DEBUG
+    SK_API int8_t      SkToS8(long);
+    SK_API uint8_t     SkToU8(size_t);
+    SK_API int16_t     SkToS16(long);
+    SK_API uint16_t    SkToU16(size_t);
+    SK_API int32_t     SkToS32(long);
+    SK_API uint32_t    SkToU32(size_t);
+#else
+    #define SkToS8(x)   ((int8_t)(x))
+    #define SkToU8(x)   ((uint8_t)(x))
+    #define SkToS16(x)  ((int16_t)(x))
+    #define SkToU16(x)  ((uint16_t)(x))
+    #define SkToS32(x)  ((int32_t)(x))
+    #define SkToU32(x)  ((uint32_t)(x))
+#endif
+
+/** Returns 0 or 1 based on the condition
+*/
+#define SkToBool(cond)  ((cond) != 0)
+
+#define SK_MaxS16   32767
+#define SK_MinS16   -32767
+#define SK_MaxU16   0xFFFF
+#define SK_MinU16   0
+#define SK_MaxS32   0x7FFFFFFF
+#define SK_MinS32   0x80000001
+#define SK_MaxU32   0xFFFFFFFF
+#define SK_MinU32   0
+#define SK_NaN32    0x80000000
+
+/** Returns true if the value can be represented with signed 16bits
+ */
+static inline bool SkIsS16(long x) {
+    return (int16_t)x == x;
+}
+
+/** Returns true if the value can be represented with unsigned 16bits
+ */
+static inline bool SkIsU16(long x) {
+    return (uint16_t)x == x;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+#ifndef SK_OFFSETOF
+    #define SK_OFFSETOF(type, field)    ((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 SkIsAlign4(x) (((x) & 3) == 0)
+
+typedef uint32_t SkFourByteTag;
+#define SkSetFourByteTag(a, b, c, d)    (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+
+/** 32 bit integer to hold a unicode value
+*/
+typedef int32_t SkUnichar;
+/** 32 bit value to hold a millisecond count
+*/
+typedef uint32_t SkMSec;
+/** 1 second measured in milliseconds
+*/
+#define SK_MSec1 1000
+/** maximum representable milliseconds
+*/
+#define SK_MSecMax 0x7FFFFFFF
+/** Returns a < b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0
+*/
+#define SkMSec_LT(a, b)     ((int32_t)(a) - (int32_t)(b) < 0)
+/** Returns a <= b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0
+*/
+#define SkMSec_LE(a, b)     ((int32_t)(a) - (int32_t)(b) <= 0)
+
+/****************************************************************************
+    The rest of these only build with C++
+*/
+#ifdef __cplusplus
+
+/** Faster than SkToBool for integral conditions. Returns 0 or 1
+*/
+static inline int Sk32ToBool(uint32_t n) {
+    return (n | (0-n)) >> 31;
+}
+
+template <typename T> inline void SkTSwap(T& a, T& b) {
+    T c(a);
+    a = b;
+    b = c;
+}
+
+static inline int32_t SkAbs32(int32_t value) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value < 0)
+        value = -value;
+    return value;
+#else
+    int32_t mask = value >> 31;
+    return (value ^ mask) - mask;
+#endif
+}
+
+static inline int32_t SkMax32(int32_t a, int32_t b) {
+    if (a < b)
+        a = b;
+    return a;
+}
+
+static inline int32_t SkMin32(int32_t a, int32_t b) {
+    if (a > b)
+        a = b;
+    return a;
+}
+
+static inline int32_t SkSign32(int32_t a) {
+    return (a >> 31) | ((unsigned) -a >> 31);
+}
+
+static inline int32_t SkFastMin32(int32_t value, int32_t max) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value > max)
+        value = max;
+    return value;
+#else
+    int diff = max - value;
+    // clear diff if it is negative (clear if value > max)
+    diff &= (diff >> 31);
+    return value + diff;
+#endif
+}
+
+/** Returns signed 32 bit value pinned between min and max, inclusively
+*/
+static inline int32_t SkPin32(int32_t value, int32_t min, int32_t max) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value < min)
+        value = min;
+    if (value > max)
+        value = max;
+#else
+    if (value < min)
+        value = min;
+    else if (value > max)
+        value = max;
+#endif
+    return value;
+}
+
+static inline uint32_t SkSetClearShift(uint32_t bits, bool cond,
+                                       unsigned shift) {
+    SkASSERT((int)cond == 0 || (int)cond == 1);
+    return (bits & ~(1 << shift)) | ((int)cond << shift);
+}
+
+static inline uint32_t SkSetClearMask(uint32_t bits, bool cond,
+                                      uint32_t mask) {
+    return cond ? bits | mask : bits & ~mask;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Use to combine multiple bits in a bitmask in a type safe way.
+ */
+template <typename T>
+T SkTBitOr(T a, T b) {
+    return (T)(a | b);
+}
+
+/**
+ *  Use to cast a pointer to a different type, and maintaining strict-aliasing
+ */
+template <typename Dst> Dst SkTCast(const void* ptr) {
+    union {
+        const void* src;
+        Dst dst;
+    } data;
+    data.src = ptr;
+    return data.dst;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/** \class SkNoncopyable
+
+SkNoncopyable is the base class for objects that may do not want to
+be copied. It hides its copy-constructor and its assignment-operator.
+*/
+class SK_API SkNoncopyable {
+public:
+    SkNoncopyable() {}
+
+private:
+    SkNoncopyable(const SkNoncopyable&);
+    SkNoncopyable& operator=(const SkNoncopyable&);
+};
+
+class SkAutoFree : SkNoncopyable {
+public:
+    SkAutoFree() : fPtr(NULL) {}
+    explicit SkAutoFree(void* ptr) : fPtr(ptr) {}
+    ~SkAutoFree() { sk_free(fPtr); }
+
+    /** Return the currently allocate buffer, or null
+    */
+    void* get() const { return fPtr; }
+
+    /** Assign a new ptr allocated with sk_malloc (or null), and return the
+        previous ptr. Note it is the caller's responsibility to sk_free the
+        returned ptr.
+    */
+    void* set(void* ptr) {
+        void* prev = fPtr;
+        fPtr = ptr;
+        return prev;
+    }
+
+    /** Transfer ownership of the current ptr to the caller, setting the
+        internal reference to null. Note the caller is reponsible for calling
+        sk_free on the returned address.
+    */
+    void* detach() { return this->set(NULL); }
+
+    /** Free the current buffer, and set the internal reference to NULL. Same
+        as calling sk_free(detach())
+    */
+    void free() {
+        sk_free(fPtr);
+        fPtr = NULL;
+    }
+
+private:
+    void* fPtr;
+    // illegal
+    SkAutoFree(const SkAutoFree&);
+    SkAutoFree& operator=(const SkAutoFree&);
+};
+
+/**
+ *  Manage an allocated block of heap memory. This object is the sole manager of
+ *  the lifetime of the block, so the caller must not call sk_free() or delete
+ *  on the block, unless detach() was called.
+ */
+class SkAutoMalloc : public SkNoncopyable {
+public:
+    explicit SkAutoMalloc(size_t size = 0) {
+        fPtr = size ? sk_malloc_throw(size) : NULL;
+        fSize = size;
+    }
+
+    ~SkAutoMalloc() {
+        sk_free(fPtr);
+    }
+
+    /**
+     *  Passed to reset to specify what happens if the requested size is smaller
+     *  than the current size (and the current block was dynamically allocated).
+     */
+    enum OnShrink {
+        /**
+         *  If the requested size is smaller than the current size, and the
+         *  current block is dynamically allocated, free the old block and
+         *  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,
+    };
+
+    /**
+     *  Reallocates the block to a new size. The ptr may or may not change.
+     */
+    void* reset(size_t size, OnShrink shrink = kAlloc_OnShrink) {
+        if (size == fSize || (kReuse_OnShrink == shrink && size < fSize)) {
+            return fPtr;
+        }
+
+        sk_free(fPtr);
+        fPtr = size ? sk_malloc_throw(size) : NULL;
+        fSize = size;
+
+        return fPtr;
+    }
+
+    /**
+     *  Releases the block back to the heap
+     */
+    void free() {
+        this->reset(0);
+    }
+
+    /**
+     *  Return the allocated block.
+     */
+    void* get() { return fPtr; }
+    const void* get() const { return fPtr; }
+
+   /** Transfer ownership of the current ptr to the caller, setting the
+       internal reference to null. Note the caller is reponsible for calling
+       sk_free on the returned address.
+    */
+    void* detach() {
+        void* ptr = fPtr;
+        fPtr = NULL;
+        fSize = 0;
+        return ptr;
+    }
+
+private:
+    void*   fPtr;
+    size_t  fSize;  // can be larger than the requested size (see kReuse)
+};
+
+/**
+ *  Manage an allocated block of memory. If the requested size is <= kSize, then
+ *  the allocation will come from the stack rather than the heap. This object
+ *  is the sole manager of the lifetime of the block, so the caller must not
+ *  call sk_free() or delete on the block.
+ */
+template <size_t kSize> class SkAutoSMalloc : SkNoncopyable {
+public:
+    /**
+     *  Creates initially empty storage. get() returns a ptr, but it is to
+     *  a zero-byte allocation. Must call reset(size) to return an allocated
+     *  block.
+     */
+    SkAutoSMalloc() {
+        fPtr = fStorage;
+        fSize = 0;
+    }
+
+    /**
+     *  Allocate a block of the specified size. If size <= kSize, then the
+     *  allocation will come from the stack, otherwise it will be dynamically
+     *  allocated.
+     */
+    explicit SkAutoSMalloc(size_t size) {
+        fPtr = fStorage;
+        fSize = 0;
+        this->reset(size);
+    }
+
+    /**
+     *  Free the allocated block (if any). If the block was small enought to
+     *  have been allocated on the stack (size <= kSize) then this does nothing.
+     */
+    ~SkAutoSMalloc() {
+        if (fPtr != (void*)fStorage) {
+            sk_free(fPtr);
+        }
+    }
+
+    /**
+     *  Return the allocated block. May return non-null even if the block is
+     *  of zero size. Since this may be on the stack or dynamically allocated,
+     *  the caller must not call sk_free() on it, but must rely on SkAutoSMalloc
+     *  to manage it.
+     */
+    void* get() const { return fPtr; }
+
+    /**
+     *  Return a new block of the requested size, freeing (as necessary) any
+     *  previously allocated block. As with the constructor, if size <= kSize
+     *  then the return block may be allocated locally, rather than from the
+     *  heap.
+     */
+    void* reset(size_t size,
+                SkAutoMalloc::OnShrink shrink = SkAutoMalloc::kAlloc_OnShrink) {
+        if (size == fSize || (SkAutoMalloc::kReuse_OnShrink == shrink &&
+                              size < fSize)) {
+            return fPtr;
+        }
+
+        if (fPtr != (void*)fStorage) {
+            sk_free(fPtr);
+        }
+
+        if (size <= kSize) {
+            fPtr = fStorage;
+        } else {
+            fPtr = sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_TEMP);
+        }
+        return fPtr;
+    }
+
+private:
+    void*       fPtr;
+    size_t      fSize;  // can be larger than the requested size (see kReuse)
+    uint32_t    fStorage[(kSize + 3) >> 2];
+};
+
+#endif /* C++ */
+
+#endif
diff --git a/legacy/include/core/SkUnPreMultiply.h b/legacy/include/core/SkUnPreMultiply.h
new file mode 100644
index 0000000..7088918
--- /dev/null
+++ b/legacy/include/core/SkUnPreMultiply.h
@@ -0,0 +1,56 @@
+
+/*
+ * 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 SkUnPreMultiply_DEFINED
+#define SkUnPreMultiply_DEFINED
+
+#include "SkColor.h"
+
+class SK_API SkUnPreMultiply {
+public:
+    typedef uint32_t Scale;
+    
+    // index this table with alpha [0..255]
+    static const Scale* GetScaleTable() {
+        return gTable;
+    }
+
+    static Scale GetScale(U8CPU alpha) {
+        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
+        }
+    */
+    static U8CPU ApplyScale(Scale scale, U8CPU component) {
+        SkASSERT(component <= 255);
+        return (scale * component + (1 << 23)) >> 24;
+    }
+    
+    static SkColor PMColorToColor(SkPMColor c);
+    
+private:
+    static const uint32_t gTable[256];
+};
+
+#endif
diff --git a/legacy/include/core/SkUnitMapper.h b/legacy/include/core/SkUnitMapper.h
new file mode 100644
index 0000000..2bd482b
--- /dev/null
+++ b/legacy/include/core/SkUnitMapper.h
@@ -0,0 +1,31 @@
+
+/*
+ * 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 SkUnitMapper_DEFINED
+#define SkUnitMapper_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+#include "SkFlattenable.h"
+
+class SkUnitMapper : public SkFlattenable {
+public:
+    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) {}
+};
+
+#endif
+
diff --git a/legacy/include/core/SkUserConfig.h b/legacy/include/core/SkUserConfig.h
new file mode 100644
index 0000000..91f8948
--- /dev/null
+++ b/legacy/include/core/SkUserConfig.h
@@ -0,0 +1,188 @@
+
+/*
+ * 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 SkUserConfig_DEFINED
+#define SkUserConfig_DEFINED
+
+/*  SkTypes.h, the root of the public header files, does the following trick:
+
+    #include "SkPreConfig.h"
+    #include "SkUserConfig.h"
+    #include "SkPostConfig.h"
+
+    SkPreConfig.h runs first, and it is responsible for initializing certain
+    skia defines.
+
+    SkPostConfig.h runs last, and its job is to just check that the final
+    defines are consistent (i.e. that we don't have mutually conflicting
+    defines).
+
+    SkUserConfig.h (this file) runs in the middle. It gets to change or augment
+    the list of flags initially set in preconfig, and then postconfig checks
+    that everything still makes sense.
+
+    Below are optional defines that add, subtract, or change default behavior
+    in Skia. Your port can locally edit this file to enable/disable flags as
+    you choose, or these can be delared on your command line (i.e. -Dfoo).
+
+    By default, this include file will always default to having all of the flags
+    commented out, so including it will have no effect.
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// 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
+
+// do this build check for other tools that still read this header
+#ifdef ANDROID
+    #include <utils/misc.h>
+#endif
+
+#define SK_USE_POSIX_THREADS
+
+/*  Scalars (the fractional value type in skia) can be implemented either as
+    floats or 16.16 integers (fixed). Exactly one of these two symbols must be
+    defined.
+*/
+#define SK_SCALAR_IS_FLOAT
+#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,
+    define this flag.
+ */
+//#define SK_SOFTWARE_FLOAT
+
+
+/*  Skia has lots of debug-only code. Often this is just null checks or other
+    parameter checking, but sometimes it can be quite intrusive (e.g. check that
+    each 32bit pixel is in premultiplied form). This code can be very useful
+    during development, but will slow things down in a shipping product.
+
+    By default, these mutually exclusive flags are defined in SkPreConfig.h,
+    based on the presence or absence of NDEBUG, but that decision can be changed
+    here.
+ */
+//#define SK_DEBUG
+//#define SK_RELEASE
+
+
+/*  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
+    SkPostConfig.h to write to an illegal address
+ */
+//#define SK_CRASH() *(int *)(uintptr_t)0 = 0
+
+
+/*  preconfig will have attempted to determine the endianness of the system,
+    but you can change these mutually exclusive flags here.
+ */
+#if __BYTE_ORDER == __BIG_ENDIAN
+    #define SK_CPU_BENDIAN
+    #undef  SK_CPU_LENDIAN
+#else
+    #define SK_CPU_LENDIAN
+    #undef  SK_CPU_BENDIAN
+#endif
+
+
+/*  Some compilers don't support long long for 64bit integers. If yours does
+    not, define this to the appropriate type.
+ */
+#define SkLONGLONG int64_t
+
+
+/*  To write debug messages to a console, skia will call SkDebugf(...) following
+    printf conventions (e.g. const char* format, ...). If you want to redirect
+    this to something other than printf, define yours here
+ */
+//#define SkDebugf(...)  MyFunction(__VA_ARGS__)
+
+/*
+ *  To specify a different default font cache limit, define this. If this is
+ *  undefined, skia will use a built-in value.
+ */
+#define SK_DEFAULT_FONT_CACHE_LIMIT   (768 * 1024)
+
+/* If defined, use CoreText instead of ATSUI on OS X.
+*/
+//#define SK_USE_MAC_CORE_TEXT
+
+
+/*  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.
+ */
+//#define SK_ZLIB_INCLUDE <zlib.h>
+
+/*  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.
+ */
+//#define SK_ALLOW_LARGE_PDF_SCALARS
+
+/*  Define this to provide font subsetter in PDF generation.
+ */
+//#define SK_SFNTLY_SUBSETTER "sfntly/subsetter/font_subsetter.h"
+
+/*  Define this to remove dimension checks on bitmaps. Not all blits will be
+    correct yet, so this is mostly for debugging the implementation.
+ */
+//#define SK_ALLOW_OVER_32K_BITMAPS
+
+/*  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
+    value (e.g. 48)
+ */
+//#define SK_MAX_SIZE_FOR_LCDTEXT     48
+
+/*  If SK_DEBUG is defined, then you can optionally define SK_SUPPORT_UNITTEST
+    which will run additional self-tests at startup. These can take a long time,
+    so this flag is optional.
+ */
+#ifdef SK_DEBUG
+    #define SK_SUPPORT_UNITTEST
+#endif
+
+/* If your system embeds skia and has complex event logging, define this
+   symbol to name a file that maps the following macros to your system's
+   equivalents:
+       SK_TRACE_EVENT0(event)
+       SK_TRACE_EVENT1(event, name1, value1)
+       SK_TRACE_EVENT2(event, name1, value1, name2, value2)
+   src/utils/SkDebugTrace.h has a trivial implementation that writes to
+   the debug output stream. If SK_USER_TRACE_INCLUDE_FILE is not defined,
+   SkTrace.h will define the above three macros to do nothing.
+*/
+//#undef SK_USER_TRACE_INCLUDE_FILE
+
+/*  Change the ordering to work in X windows.
+ */
+#ifdef SK_SAMPLES_FOR_X
+        #define SK_R32_SHIFT    16
+        #define SK_G32_SHIFT    8
+        #define SK_B32_SHIFT    0
+        #define SK_A32_SHIFT    24
+#endif
+
+#endif
diff --git a/legacy/include/core/SkUtils.h b/legacy/include/core/SkUtils.h
new file mode 100644
index 0000000..b700b96
--- /dev/null
+++ b/legacy/include/core/SkUtils.h
@@ -0,0 +1,136 @@
+
+/*
+ * 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 SkUtils_DEFINED
+#define SkUtils_DEFINED
+
+#include "SkTypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Similar to memset(), but it assigns a 16bit value into the buffer.
+    @param buffer   The memory to have value copied into it
+    @param value    The 16bit value to be copied into buffer
+    @param count    The number of times value should be copied into the buffer.
+*/
+void sk_memset16_portable(uint16_t dst[], uint16_t value, int count);
+typedef void (*SkMemset16Proc)(uint16_t dst[], uint16_t value, int count);
+SkMemset16Proc SkMemset16GetPlatformProc();
+
+/** Similar to memset(), but it assigns a 32bit value into the buffer.
+    @param buffer   The memory to have value copied into it
+    @param value    The 32bit value to be copied into buffer
+    @param count    The number of times value should be copied into the buffer.
+*/
+void sk_memset32_portable(uint32_t dst[], uint32_t value, int count);
+typedef void (*SkMemset32Proc)(uint32_t dst[], uint32_t value, int count);
+SkMemset32Proc SkMemset32GetPlatformProc();
+
+#ifndef sk_memset16
+extern SkMemset16Proc sk_memset16;
+#endif
+
+#ifndef sk_memset32
+extern SkMemset32Proc sk_memset32;
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define kMaxBytesInUTF8Sequence     4
+
+#ifdef SK_DEBUG
+    int SkUTF8_LeadByteToCount(unsigned c);
+#else
+    #define SkUTF8_LeadByteToCount(c)   ((((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1)
+#endif
+
+inline int SkUTF8_CountUTF8Bytes(const char utf8[]) {
+    SkASSERT(utf8);
+    return SkUTF8_LeadByteToCount(*(const uint8_t*)utf8);
+}
+
+int         SkUTF8_CountUnichars(const char utf8[]);
+int         SkUTF8_CountUnichars(const char utf8[], size_t byteLength);
+SkUnichar   SkUTF8_ToUnichar(const char utf8[]);
+SkUnichar   SkUTF8_NextUnichar(const char**);
+SkUnichar   SkUTF8_PrevUnichar(const char**);
+
+/** Return the number of bytes need to convert a unichar
+    into a utf8 sequence. Will be 1..kMaxBytesInUTF8Sequence,
+    or 0 if uni is illegal.
+*/
+size_t      SkUTF8_FromUnichar(SkUnichar uni, char utf8[] = NULL);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SkUTF16_IsHighSurrogate(c)  (((c) & 0xFC00) == 0xD800)
+#define SkUTF16_IsLowSurrogate(c)   (((c) & 0xFC00) == 0xDC00)
+
+int SkUTF16_CountUnichars(const uint16_t utf16[]);
+int SkUTF16_CountUnichars(const uint16_t utf16[],
+                                  int numberOf16BitValues);
+// returns the current unichar and then moves past it (*p++)
+SkUnichar SkUTF16_NextUnichar(const uint16_t**);
+// this guy backs up to the previus unichar value, and returns it (*--p)
+SkUnichar SkUTF16_PrevUnichar(const uint16_t**);
+size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t utf16[] = NULL);
+
+size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues,
+                           char utf8[] = NULL);
+
+inline bool SkUnichar_IsVariationSelector(SkUnichar uni) {
+/*  The 'true' ranges are:
+ *      0x180B  <= uni <=  0x180D
+ *      0xFE00  <= uni <=  0xFE0F
+ *      0xE0100 <= uni <= 0xE01EF
+ */
+    if (uni < 0x180B || uni > 0xE01EF) {
+        return false;
+    }
+    if ((uni > 0x180D && uni < 0xFE00) || (uni > 0xFE0F && uni < 0xE0100)) {
+        return false;
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoTrace {
+public:
+    /** NOTE: label contents are not copied, just the ptr is
+        retained, so DON'T DELETE IT.
+    */
+    SkAutoTrace(const char label[]) : fLabel(label) {
+        SkDebugf("--- trace: %s Enter\n", fLabel);
+    }
+    ~SkAutoTrace() {
+        SkDebugf("--- trace: %s Leave\n", fLabel);
+    }
+private:
+    const char* fLabel;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoMemoryUsageProbe {
+public:
+    /** Record memory usage in constructor, and dump the result
+        (delta and current total) in the destructor, with the optional
+        label. NOTE: label contents are not copied, just the ptr is
+        retained, so DON'T DELETE IT.
+    */
+    SkAutoMemoryUsageProbe(const char label[]);
+    ~SkAutoMemoryUsageProbe();
+private:
+    const char* fLabel;
+    size_t      fBytesAllocated;
+};
+
+#endif
+
diff --git a/legacy/include/core/SkWriter32.h b/legacy/include/core/SkWriter32.h
new file mode 100644
index 0000000..d7159ff
--- /dev/null
+++ b/legacy/include/core/SkWriter32.h
@@ -0,0 +1,147 @@
+
+/*
+ * 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 SkWriter32_DEFINED
+#define SkWriter32_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkScalar.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+
+class SkStream;
+class SkWStream;
+
+class SkWriter32 : SkNoncopyable {
+public:
+    SkWriter32(size_t minSize)
+        : fMinSize(minSize),
+          fSize(0),
+          fSingleBlock(NULL),
+          fSingleBlockSize(0),
+          fHead(NULL),
+          fTail(NULL) {
+    }
+    ~SkWriter32();
+
+    /**
+     *  Returns the single block backing the writer, or NULL if the memory is
+     *  to be dynamically allocated.
+     */
+    void* getSingleBlock() const { return fSingleBlock; }
+
+    /**
+     *  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);
+                    
+    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 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;
+    }
+    
+    // write count bytes (must be a multiple of 4)
+    void writeMul4(const void* values, size_t size) {
+        this->write(values, size);
+    }
+
+    /**
+     *  Write size bytes from values. size must be a multiple of 4, though
+     *  values need not be 4-byte aligned.
+     */
+    void write(const void* values, size_t size) {
+        SkASSERT(SkAlign4(size) == size);
+        // if we could query how much is avail in the current block, we might
+        // copy that much, and then alloc the rest. That would reduce the waste
+        // in the current block
+        memcpy(this->reserve(size), values, size);
+    }
+    
+    void writePad(const void* src, size_t size);
+
+    /**
+     *  Writes a string to the writer, which can be retrieved with
+     *  SkReader32::readString().
+     *  The length can be specified, or if -1 is passed, it will be computed by
+     *  calling strlen(). The length must be < 0xFFFF
+     */
+    void writeString(const char* str, size_t len = (size_t)-1);
+
+    /**
+     *  Computes the size (aligned to multiple of 4) need to write the string
+     *  in a call to writeString(). If the length is not specified, it will be
+     *  computed by calling strlen().
+     */
+    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);
+    
+    // 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:
+    size_t      fMinSize;
+    uint32_t    fSize;
+
+    char*       fSingleBlock;
+    uint32_t    fSingleBlockSize;
+    
+    struct Block;
+    Block*  fHead;
+    Block*  fTail;
+
+    Block* newBlock(size_t bytes);
+};
+
+#endif
diff --git a/legacy/include/core/SkXfermode.h b/legacy/include/core/SkXfermode.h
new file mode 100644
index 0000000..0d1c207
--- /dev/null
+++ b/legacy/include/core/SkXfermode.h
@@ -0,0 +1,238 @@
+
+/*
+ * 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 SkXfermode_DEFINED
+#define SkXfermode_DEFINED
+
+#include "SkFlattenable.h"
+#include "SkColor.h"
+
+/** \class SkXfermode
+
+    SkXfermode is the base class for objects that are called to implement custom
+    "transfer-modes" in the drawing pipeline. The static function Create(Modes)
+    can be called to return an instance of any of the predefined subclasses as
+    specified in the Modes enum. When an SkXfermode is assigned to an SkPaint,
+    then objects drawn with that paint have the xfermode applied.
+*/
+class SK_API SkXfermode : public SkFlattenable {
+public:
+    SkXfermode() {}
+
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                          const SkAlpha aa[]);
+    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+
+    /** Enum of possible coefficients to describe some xfermodes
+     */
+    enum Coeff {
+        kZero_Coeff,    /** 0 */
+        kOne_Coeff,     /** 1 */
+        kSC_Coeff,      /** src color */
+        kISC_Coeff,     /** inverse src color (i.e. 1 - sc) */
+        kDC_Coeff,      /** dst color */
+        kIDC_Coeff,     /** inverse dst color (i.e. 1 - dc) */
+        kSA_Coeff,      /** src alpha */
+        kISA_Coeff,     /** inverse src alpha (i.e. 1 - sa) */
+        kDA_Coeff,      /** dst alpha */
+        kIDA_Coeff,     /** inverse dst alpha (i.e. 1 - da) */
+
+        kCoeffCount
+    };
+
+    /** If the xfermode can be expressed as an equation using the coefficients
+        in Coeff, then asCoeff() returns true, and sets (if not null) src and
+        dst accordingly.
+
+            result = src_coeff * src_color + dst_coeff * dst_color;
+
+        As examples, here are some of the porterduff coefficients
+
+        MODE        SRC_COEFF       DST_COEFF
+        clear       zero            zero
+        src         one             zero
+        dst         zero            one
+        srcover     one             isa
+        dstover     ida             one
+     */
+    virtual bool asCoeff(Coeff* src, Coeff* dst);
+
+    /**
+     *  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);
+
+    /** List of predefined xfermodes.
+        The algebra for the modes uses the following symbols:
+        Sa, Sc  - source alpha and color
+        Da, Dc - destination alpha and color (before compositing)
+        [a, c] - Resulting (alpha, color) values
+        For these equations, the colors are in premultiplied state.
+        If no xfermode is specified, kSrcOver is assumed.
+     */
+    enum Mode {
+        kClear_Mode,    //!< [0, 0]
+        kSrc_Mode,      //!< [Sa, Sc]
+        kDst_Mode,      //!< [Da, Dc]
+        kSrcOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc]
+        kDstOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc]
+        kSrcIn_Mode,    //!< [Sa * Da, Sc * Da]
+        kDstIn_Mode,    //!< [Sa * Da, Sa * Dc]
+        kSrcOut_Mode,   //!< [Sa * (1 - Da), Sc * (1 - Da)]
+        kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+        kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+        kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+        kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+
+        // all remaining modes are defined in the SVG Compositing standard
+        // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
+        kPlus_Mode,
+        kMultiply_Mode, 
+        
+        // all above modes can be expressed as pair of src/dst Coeffs
+        kCoeffModesCnt, 
+        
+        kScreen_Mode = kCoeffModesCnt,
+        kOverlay_Mode,
+        kDarken_Mode,
+        kLighten_Mode,
+        kColorDodge_Mode,
+        kColorBurn_Mode,
+        kHardLight_Mode,
+        kSoftLight_Mode,
+        kDifference_Mode,
+        kExclusion_Mode,
+
+        kLastMode = kExclusion_Mode
+    };
+
+    /**
+     *  If the xfermode is one of the modes in the Mode enum, then asMode()
+     *  returns true and sets (if not null) mode accordingly. Otherwise it
+     *  returns false and ignores the mode parameter.
+     */
+    virtual bool asMode(Mode* mode);
+
+    /**
+     *  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);
+
+    /**
+     *  Returns true if the xfermode claims to be the specified Mode. This works
+     *  correctly even if the xfermode is NULL (which equates to kSrcOver.) Thus
+     *  you can say this without checking for a null...
+     *
+     *  If (SkXfermode::IsMode(paint.getXfermode(),
+     *                         SkXfermode::kDstOver_Mode)) {
+     *      ...
+     *  }
+     */
+    static bool IsMode(SkXfermode* xfer, Mode mode);
+
+    /** Return an SkXfermode object for the specified mode.
+     */
+    static SkXfermode* Create(Mode mode);
+
+    /** Return a function pointer to a routine that applies the specified
+        porter-duff transfer mode.
+     */
+    static SkXfermodeProc GetProc(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 GetProc16(Mode mode, SkColor srcColor);
+
+    /**
+     *  If the specified mode can be represented by a pair of Coeff, then return
+     *  true and set (if not NULL) the corresponding coeffs. If the mode is
+     *  not representable as a pair of Coeffs, return false and ignore the
+     *  src and dst parameters.
+     */
+    static bool ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst);
+
+    // DEPRECATED: call AsMode(...)
+    static bool IsMode(SkXfermode* xfer, Mode* mode) {
+        return AsMode(xfer, mode);
+    }
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+protected:
+    SkXfermode(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {}
+
+    /** The default implementation of xfer32/xfer16/xferA8 in turn call this
+        method, 1 color at a time (upscaled to a SkPMColor). The default
+        implmentation of this method just returns dst. If performance is
+        important, your subclass should override xfer32/xfer16/xferA8 directly.
+
+        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);
+
+private:
+    enum {
+        kModeCount = kLastMode + 1
+    };
+    typedef SkFlattenable INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** \class SkProcXfermode
+
+    SkProcXfermode is a xfermode that applies the specified proc to its colors.
+    This class is not exported to java.
+*/
+class SkProcXfermode : public SkXfermode {
+public:
+    SkProcXfermode(SkXfermodeProc proc) : fProc(proc) {}
+
+    // overrides from SkXfermode
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) SK_OVERRIDE;
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) SK_OVERRIDE;
+    virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                          const SkAlpha aa[]) SK_OVERRIDE;
+    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) SK_OVERRIDE;
+
+    // overrides from SkFlattenable
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+    virtual void    flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+
+protected:
+    SkProcXfermode(SkFlattenableReadBuffer&);
+
+    // allow subclasses to update this after we unflatten
+    void setProc(SkXfermodeProc proc) {
+        fProc = proc;
+    }
+
+private:
+    SkXfermodeProc  fProc;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkProcXfermode, (buffer)); }
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/Sk1DPathEffect.h b/legacy/include/effects/Sk1DPathEffect.h
new file mode 100644
index 0000000..814e547
--- /dev/null
+++ b/legacy/include/effects/Sk1DPathEffect.h
@@ -0,0 +1,88 @@
+
+/*
+ * 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 Sk1DPathEffect_DEFINED
+#define Sk1DPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+#include "SkPath.h"
+
+class SkPathMeasure;
+
+//  This class is not exported to java.
+class Sk1DPathEffect : public SkPathEffect {
+public:
+    //  override from SkPathEffect
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+protected:
+    /** Called at the start of each contour, returns the initial offset
+        into that contour.
+    */
+    virtual SkScalar begin(SkScalar contourLength) = 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;
+
+private:
+    typedef SkPathEffect INHERITED;
+};
+
+class 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
+        @param phase distance (mod advance) along path for its initial position
+        @param style how to transform path at each point (based on the current
+                     position and tangent)
+    */
+    SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style);
+
+    // override from SkPathEffect
+    virtual bool filterPath(SkPath*, const SkPath&, SkScalar* width) SK_OVERRIDE;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkPath1DPathEffect, (buffer));
+    }
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+protected:
+    SkPath1DPathEffect(SkFlattenableReadBuffer& buffer);
+
+    // 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; }
+    
+private:
+    SkPath      fPath;          // copied from constructor
+    SkScalar    fAdvance;       // copied from constructor
+    SkScalar    fInitialOffset; // computed from phase
+    Style       fStyle;         // copied from constructor
+
+    typedef Sk1DPathEffect INHERITED;
+};
+
+
+#endif
diff --git a/legacy/include/effects/Sk2DPathEffect.h b/legacy/include/effects/Sk2DPathEffect.h
new file mode 100644
index 0000000..b5d7fbb
--- /dev/null
+++ b/legacy/include/effects/Sk2DPathEffect.h
@@ -0,0 +1,90 @@
+
+/*
+ * 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 Sk2DPathEffect_DEFINED
+#define Sk2DPathEffect_DEFINED
+
+#include "SkPath.h"
+#include "SkPathEffect.h"
+#include "SkMatrix.h"
+
+class Sk2DPathEffect : public SkPathEffect {
+public:
+    Sk2DPathEffect(const SkMatrix& mat);
+
+    // overrides
+    virtual bool filterPath(SkPath*, const SkPath&, SkScalar* width) SK_OVERRIDE;
+
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    virtual Factory getFactory() SK_OVERRIDE;
+
+protected:
+    /** New virtual, to be overridden by subclasses.
+        This is called once from filterPath, and provides the
+        uv parameter bounds for the path. Subsequent calls to
+        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);
+
+    /** 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);
+
+    const SkMatrix& getMatrix() const { return fMatrix; }
+
+    // protected so that subclasses can call this during unflattening
+    Sk2DPathEffect(SkFlattenableReadBuffer&);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+private:
+    SkMatrix    fMatrix, fInverse;
+    // illegal
+    Sk2DPathEffect(const Sk2DPathEffect&);
+    Sk2DPathEffect& operator=(const Sk2DPathEffect&);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+    friend class Sk2DPathEffectBlitter;
+    typedef SkPathEffect INHERITED;
+};
+
+class 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()
+
+protected:
+    SkPath2DPathEffect(SkFlattenableReadBuffer& buffer);
+
+    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    virtual Factory getFactory() SK_OVERRIDE;
+    virtual void next(const SkPoint&, int u, int v, SkPath* dst) SK_OVERRIDE;
+
+private:
+    SkPath  fPath;
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+
+#endif
diff --git a/legacy/include/effects/SkArithmeticMode.h b/legacy/include/effects/SkArithmeticMode.h
new file mode 100644
index 0000000..70d660f
--- /dev/null
+++ b/legacy/include/effects/SkArithmeticMode.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkArithmeticMode_DEFINED
+#define SkArithmeticMode_DEFINED
+
+#include "SkXfermode.h"
+
+class SkArithmeticMode : public SkXfermode {
+public:
+    /**
+     *  result = clamp[k1 * src * dst + k2 * src + k3 * dst + k4]
+     *
+     *  src and dst are treated as being [0.0 .. 1.0]. The polynomial is
+     *  evaluated on their unpremultiplied components.
+     *
+     *  k1=k2=k3=0, k4=1.0 results in returning opaque white
+     *  k1=k3=k4=0, k2=1.0 results in returning the src
+     *  k1=k2=k4=0, k3=1.0 results in returning the dst
+     */
+    static SkXfermode* Create(SkScalar k1, SkScalar k2,
+                              SkScalar k3, SkScalar k4);
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkAvoidXfermode.h b/legacy/include/effects/SkAvoidXfermode.h
new file mode 100644
index 0000000..398eaea
--- /dev/null
+++ b/legacy/include/effects/SkAvoidXfermode.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 SkAvoidXfermode_DEFINED
+#define SkAvoidXfermode_DEFINED
+
+#include "SkXfermode.h"
+
+/** \class SkAvoidXfermode
+
+    This xfermode will draw the src everywhere except on top of the specified
+    color.
+*/
+class SkAvoidXfermode : public SkXfermode {
+public:
+    enum Mode {
+        kAvoidColor_Mode,   //!< draw everywhere except on the opColor
+        kTargetColor_Mode   //!< draw only on top of the opColor
+    };
+
+    /** This xfermode draws, or doesn't draw, based on the destination's
+        distance from an op-color.
+
+        There are two modes, and each mode interprets a tolerance value.
+
+        Avoid: In this mode, drawing is allowed only on destination pixels that
+               are different from the op-color.
+               Tolerance near 0: avoid any colors even remotely similar to the op-color
+               Tolerance near 255: avoid only colors nearly identical to the op-color
+
+        Target: In this mode, drawing only occurs on destination pixels that
+                are similar to the op-color
+                Tolerance near 0: draw only on colors that are nearly identical to the op-color
+                Tolerance near 255: draw on any colors even remotely similar to the op-color
+     */
+    SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode);
+
+    // overrides from SkXfermode
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) SK_OVERRIDE;
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) SK_OVERRIDE;
+    virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                          const SkAlpha aa[]) SK_OVERRIDE;
+    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) 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()
+
+protected:
+    SkAvoidXfermode(SkFlattenableReadBuffer&);
+
+private:
+    SkColor     fOpColor;
+    uint32_t    fDistMul;   // x.14
+    Mode        fMode;
+
+    static SkFlattenable* Create(SkFlattenableReadBuffer&);
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/SkBlurDrawLooper.h b/legacy/include/effects/SkBlurDrawLooper.h
new file mode 100644
index 0000000..be1a78e
--- /dev/null
+++ b/legacy/include/effects/SkBlurDrawLooper.h
@@ -0,0 +1,76 @@
+
+/*
+ * 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 SkBlurDrawLooper_DEFINED
+#define SkBlurDrawLooper_DEFINED
+
+#include "SkDrawLooper.h"
+#include "SkColor.h"
+
+class SkMaskFilter;
+class SkColorFilter;
+
+/** \class SkBlurDrawLooper
+    This class draws a shadow of the object (possibly offset), and then draws
+    the original object in its original position.
+    should there be an option to just draw the shadow/blur layer? webkit?
+*/
+class SK_API SkBlurDrawLooper : public SkDrawLooper {
+public:
+    enum BlurFlags {
+        kNone_BlurFlag = 0x00,
+        /** 
+            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
+    };
+
+    SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color, 
+                     uint32_t flags = kNone_BlurFlag);
+    virtual ~SkBlurDrawLooper();
+
+    // overrides from SkDrawLooper
+    virtual void init(SkCanvas*);
+    virtual bool next(SkCanvas*, SkPaint* paint);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkBlurDrawLooper, (buffer));
+    }
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+protected:
+    SkBlurDrawLooper(SkFlattenableReadBuffer&);
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    SkMaskFilter*   fBlur;
+    SkColorFilter*  fColorFilter;
+    SkScalar        fDx, fDy;
+    SkColor         fBlurColor;
+    uint32_t        fBlurFlags;  
+
+    enum State {
+        kBeforeEdge,
+        kAfterEdge,
+        kDone
+    };
+    State   fState;
+    
+    typedef SkDrawLooper INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/SkBlurImageFilter.h b/legacy/include/effects/SkBlurImageFilter.h
new file mode 100644
index 0000000..4c9798b
--- /dev/null
+++ b/legacy/include/effects/SkBlurImageFilter.h
@@ -0,0 +1,40 @@
+/*
+ * 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 SkBlurImageFilter_DEFINED
+#define SkBlurImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+
+class SK_API SkBlurImageFilter : public SkImageFilter {
+public:
+    SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY);
+
+    virtual bool asABlur(SkSize* sigma) const SK_OVERRIDE;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkBlurImageFilter, (buffer));
+    }
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+protected:
+    explicit SkBlurImageFilter(SkFlattenableReadBuffer& buffer);
+
+    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; }
+
+private:
+    SkSize   fSigma;
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkBlurMaskFilter.h b/legacy/include/effects/SkBlurMaskFilter.h
new file mode 100644
index 0000000..9e85d87
--- /dev/null
+++ b/legacy/include/effects/SkBlurMaskFilter.h
@@ -0,0 +1,65 @@
+
+/*
+ * 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 SkBlurMaskFilter_DEFINED
+#define SkBlurMaskFilter_DEFINED
+
+// we include this since our callers will need to at least be able to ref/unref
+#include "SkMaskFilter.h"
+#include "SkScalar.h"
+
+class SK_API SkBlurMaskFilter {
+public:
+    enum BlurStyle {
+        kNormal_BlurStyle,  //!< fuzzy inside and outside
+        kSolid_BlurStyle,   //!< solid inside, fuzzy outside
+        kOuter_BlurStyle,   //!< nothing inside, fuzzy outside
+        kInner_BlurStyle,   //!< fuzzy inside, nothing outside
+
+        kBlurStyleCount
+    };
+
+    enum BlurFlags {
+        kNone_BlurFlag = 0x00,
+        /** The blur layer's radius is not affected by transforms */
+        kIgnoreTransform_BlurFlag   = 0x01,
+        /** Use a smother, higher qulity blur algorithm */
+        kHighQuality_BlurFlag       = 0x02,
+        /** mask for all blur flags */
+        kAll_BlurFlag = 0x03
+    };
+
+    /** Create a blur maskfilter.
+        @param radius   The radius to extend the blur from the original mask. Must be > 0.
+        @param style    The BlurStyle to use
+        @param flags    Flags to use - defaults to none
+        @return The new blur maskfilter
+    */
+    static SkMaskFilter* Create(SkScalar radius, BlurStyle style, 
+                                uint32_t flags = kNone_BlurFlag);
+
+    /** Create an emboss maskfilter
+        @param direction    array of 3 scalars [x, y, z] specifying the direction of the light source
+        @param ambient      0...1 amount of ambient light
+        @param specular     coefficient for specular highlights (e.g. 8)
+        @param blurRadius   amount to blur before applying lighting (e.g. 3)
+        @return the emboss maskfilter
+    */
+    static SkMaskFilter* CreateEmboss(  const SkScalar direction[3],
+                                        SkScalar ambient, SkScalar specular,
+                                        SkScalar blurRadius);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+private:
+    SkBlurMaskFilter(); // can't be instantiated
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkColorMatrix.h b/legacy/include/effects/SkColorMatrix.h
new file mode 100644
index 0000000..ee383db
--- /dev/null
+++ b/legacy/include/effects/SkColorMatrix.h
@@ -0,0 +1,46 @@
+
+/*
+ * Copyright 2007 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 SkColorMatrix_DEFINED
+#define SkColorMatrix_DEFINED
+
+#include "SkScalar.h"
+
+class SkColorMatrix {
+public:
+    SkScalar    fMat[20];
+    
+    void setIdentity();
+    void setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                  SkScalar aScale = SK_Scalar1);
+    void preScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                  SkScalar aScale = SK_Scalar1);
+    void postScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                   SkScalar aScale = SK_Scalar1);
+
+    enum Axis {
+        kR_Axis = 0,
+        kG_Axis = 1,
+        kB_Axis = 2
+    };
+    void setRotate(Axis, SkScalar degrees);
+    void setSinCos(Axis, SkScalar sine, SkScalar cosine);
+    void preRotate(Axis, SkScalar degrees);
+    void postRotate(Axis, SkScalar degrees);
+
+    void setConcat(const SkColorMatrix& a, const SkColorMatrix& b);
+    void preConcat(const SkColorMatrix& mat) { this->setConcat(*this, mat); }
+    void postConcat(const SkColorMatrix& mat) { this->setConcat(mat, *this); }
+
+    void setSaturation(SkScalar sat);
+    void setRGB2YUV();
+    void setYUV2RGB();
+};
+
+#endif
diff --git a/legacy/include/effects/SkColorMatrixFilter.h b/legacy/include/effects/SkColorMatrixFilter.h
new file mode 100644
index 0000000..1475258
--- /dev/null
+++ b/legacy/include/effects/SkColorMatrixFilter.h
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright 2007 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 SkColorMatrixFilter_DEFINED
+#define SkColorMatrixFilter_DEFINED
+
+#include "SkColorFilter.h"
+#include "SkColorMatrix.h"
+
+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;
+
+    struct State {
+        int32_t fArray[20];
+        int     fShift;
+        int32_t fResult[4];
+    };
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+protected:
+    // overrides for SkFlattenable
+    virtual Factory getFactory();
+
+    SkColorMatrixFilter(SkFlattenableReadBuffer& buffer);
+
+private:
+
+    typedef void (*Proc)(State*, unsigned r, unsigned g, unsigned b,
+                         unsigned a);
+
+    Proc        fProc;
+    State       fState;
+    uint32_t    fFlags;
+
+    void setup(const SkScalar array[20]);
+
+    typedef SkColorFilter INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/SkCornerPathEffect.h b/legacy/include/effects/SkCornerPathEffect.h
new file mode 100644
index 0000000..990bad0
--- /dev/null
+++ b/legacy/include/effects/SkCornerPathEffect.h
@@ -0,0 +1,52 @@
+
+/*
+ * 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 SkCornerPathEffect_DEFINED
+#define SkCornerPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+
+/** \class SkCornerPathEffect
+
+    SkCornerPathEffect is a subclass of SkPathEffect that can turn sharp corners
+    into various treatments (e.g. rounded corners)
+*/
+class SK_API SkCornerPathEffect : public SkPathEffect {
+public:
+    /** radius must be > 0 to have an effect. It specifies the distance from each corner
+        that should be "rounded".
+    */
+    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);
+
+    // 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()
+
+protected:
+    SkCornerPathEffect(SkFlattenableReadBuffer&);
+
+private:
+    SkScalar    fRadius;
+    
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkDashPathEffect.h b/legacy/include/effects/SkDashPathEffect.h
new file mode 100644
index 0000000..6d34910
--- /dev/null
+++ b/legacy/include/effects/SkDashPathEffect.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 SkDashPathEffect_DEFINED
+#define SkDashPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+
+/** \class SkDashPathEffect
+
+    SkDashPathEffect is a subclass of SkPathEffect that implements dashing
+*/
+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
+    */
+    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);
+
+    // 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()
+
+protected:
+    SkDashPathEffect(SkFlattenableReadBuffer&);
+    
+private:
+    SkScalar*   fIntervals;
+    int32_t     fCount;
+    // computed from phase
+    SkScalar    fInitialDashLength;
+    int32_t     fInitialDashIndex;
+    SkScalar    fIntervalLength;
+    bool        fScaleToFit;
+
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkDiscretePathEffect.h b/legacy/include/effects/SkDiscretePathEffect.h
new file mode 100644
index 0000000..5369ddb
--- /dev/null
+++ b/legacy/include/effects/SkDiscretePathEffect.h
@@ -0,0 +1,51 @@
+
+/*
+ * 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 SkDiscretePathEffect_DEFINED
+#define SkDiscretePathEffect_DEFINED
+
+#include "SkPathEffect.h"
+
+/** \class SkDiscretePathEffect
+
+    This path effect chops a path into discrete segments, and randomly displaces them.
+*/
+class 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.
+        Note: works on filled or framed paths
+    */
+    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);
+
+    // 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()
+
+protected:
+    SkDiscretePathEffect(SkFlattenableReadBuffer&);
+
+private:
+    SkScalar fSegLength, fPerterb;
+    
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkDrawExtraPathEffect.h b/legacy/include/effects/SkDrawExtraPathEffect.h
new file mode 100644
index 0000000..c7611f0
--- /dev/null
+++ b/legacy/include/effects/SkDrawExtraPathEffect.h
@@ -0,0 +1,15 @@
+
+/*
+ * 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 SK_DRAW_EXTRA_PATH_EFFECT_H
+#define SK_DRAW_EXTRA_PATH_EFFECT_H
+class SkAnimator;
+void InitializeSkExtraPathEffects(SkAnimator* animator);
+#endif
+
diff --git a/include/effects/SkEffects.h b/legacy/include/effects/SkEffects.h
similarity index 100%
rename from include/effects/SkEffects.h
rename to legacy/include/effects/SkEffects.h
diff --git a/legacy/include/effects/SkEmbossMaskFilter.h b/legacy/include/effects/SkEmbossMaskFilter.h
new file mode 100644
index 0000000..257e19a
--- /dev/null
+++ b/legacy/include/effects/SkEmbossMaskFilter.h
@@ -0,0 +1,59 @@
+
+/*
+ * 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 SkEmbossMaskFilter_DEFINED
+#define SkEmbossMaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+
+/** \class SkEmbossMaskFilter
+
+    This mask filter creates a 3D emboss look, by specifying a light and blur amount.
+*/
+class SkEmbossMaskFilter : public SkMaskFilter {
+public:
+    struct Light {
+        SkScalar    fDirection[3];  // x,y,z
+        uint16_t    fPad;
+        uint8_t     fAmbient;
+        uint8_t     fSpecular;      // exponent, 4.4 right now
+    };
+
+    SkEmbossMaskFilter(const Light& light, SkScalar blurRadius);
+
+    // overrides from SkMaskFilter
+    //  This method is not exported to java.
+    virtual SkMask::Format getFormat();
+    //  This method is not exported to java.
+    virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
+                            SkIPoint* margin);
+
+    // 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()
+
+protected:
+    SkEmbossMaskFilter(SkFlattenableReadBuffer&);
+
+private:
+    Light       fLight;
+    SkScalar    fBlurRadius;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+    
+    typedef SkMaskFilter INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkGradientShader.h b/legacy/include/effects/SkGradientShader.h
new file mode 100644
index 0000000..3232703
--- /dev/null
+++ b/legacy/include/effects/SkGradientShader.h
@@ -0,0 +1,120 @@
+
+/*
+ * 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 SkGradientShader_DEFINED
+#define SkGradientShader_DEFINED
+
+#include "SkShader.h"
+
+class SkUnitMapper;
+
+/** \class SkGradientShader
+
+    SkGradientShader hosts factories for creating subclasses of SkShader that
+    render linear and radial gradients.
+*/
+class SK_API SkGradientShader {
+public:
+    /** Returns a shader that generates a linear gradient between the two
+        specified points.
+        <p />
+        CreateLinear returns a shader with a reference count of 1.
+        The caller should decrement the shader's reference count when done with the shader.
+        It is an error for count to be < 2.
+        @param  pts The start and end points for the gradient.
+        @param  colors  The array[count] of colors, to be distributed between the two points
+        @param  pos     May be NULL. array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the 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  mode    The tiling mode
+        @param  mapper  May be NULL. Callback to modify the spread of the colors.
+    */
+    static SkShader* CreateLinear(  const SkPoint pts[2],
+                                    const SkColor colors[], const SkScalar pos[], int count,
+                                    SkShader::TileMode mode,
+                                    SkUnitMapper* mapper = NULL);
+
+    /** Returns a shader that generates a radial gradient given the center and radius.
+        <p />
+        CreateRadial returns a shader with a reference count of 1.
+        The caller should decrement the shader's reference count when done with the shader.
+        It is an error for colorCount to be < 2, or for radius to be <= 0.
+        @param  center  The center of the circle for this gradient
+        @param  radius  Must be positive. The radius of the circle for this gradient
+        @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
+        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the center and edge of the circle.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
+        @param  mode    The tiling mode
+        @param  mapper  May be NULL. Callback to modify the spread of the colors.
+    */
+    static SkShader* CreateRadial(  const SkPoint& center, SkScalar radius,
+                                    const SkColor colors[], const SkScalar pos[], int count,
+                                    SkShader::TileMode mode,
+                                    SkUnitMapper* mapper = NULL);
+
+    /** Returns a shader that generates a radial gradient given the start position, start radius, end position and end radius.
+        <p />
+        CreateTwoPointRadial returns a shader with a reference count of 1.
+        The caller should decrement the shader's reference count when done with the shader.
+        It is an error for colorCount to be < 2, for startRadius or endRadius to be < 0, or for
+        startRadius to be equal to endRadius.
+        @param  start   The center of the start circle for this gradient
+        @param  startRadius  Must be positive.  The radius of the start circle for this gradient.
+        @param  end     The center of the end circle for this gradient
+        @param  endRadius  Must be positive. The radius of the end circle for this gradient.
+        @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
+        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the center and edge of the circle.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
+        @param  mode    The tiling mode
+        @param  mapper  May be NULL. Callback to modify the spread of the colors.
+    */
+    static SkShader* CreateTwoPointRadial(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.
+        The caller should decrement the shader's reference count when done with the shader.
+        It is an error for colorCount to be < 2.
+        @param  cx      The X coordinate of the center of the sweep
+        @param  cx      The Y coordinate of the center of the sweep
+        @param  colors  The array[count] of colors, to be distributed around the center.
+        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the center and edge of the circle.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
+        @param  mapper  May be NULL. Callback to modify the spread of the colors.
+    */
+    static SkShader* CreateSweep(SkScalar cx, SkScalar cy,
+                                 const SkColor colors[], const SkScalar pos[],
+                                 int count, SkUnitMapper* mapper = NULL);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+};
+
+#endif
+
diff --git a/include/effects/SkGroupShape.h b/legacy/include/effects/SkGroupShape.h
similarity index 100%
rename from include/effects/SkGroupShape.h
rename to legacy/include/effects/SkGroupShape.h
diff --git a/legacy/include/effects/SkKernel33MaskFilter.h b/legacy/include/effects/SkKernel33MaskFilter.h
new file mode 100644
index 0000000..2b9e6d4
--- /dev/null
+++ b/legacy/include/effects/SkKernel33MaskFilter.h
@@ -0,0 +1,65 @@
+
+/*
+ * 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 SkKernel33MaskFilter_DEFINED
+#define SkKernel33MaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+
+class 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*);
+
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& wb);
+
+protected:
+    SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb);
+
+private:
+    int fPercent256;
+    
+    typedef SkMaskFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class 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();
+    
+private:
+    int fKernel[3][3];
+    int fShift;
+
+    SkKernel33MaskFilter(SkFlattenableReadBuffer& rb);
+    static SkFlattenable* Create(SkFlattenableReadBuffer& rb);
+    
+    typedef SkKernel33ProcMaskFilter INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/SkLayerDrawLooper.h b/legacy/include/effects/SkLayerDrawLooper.h
new file mode 100644
index 0000000..697d7b2
--- /dev/null
+++ b/legacy/include/effects/SkLayerDrawLooper.h
@@ -0,0 +1,143 @@
+
+/*
+ * 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
+
+#include "SkDrawLooper.h"
+#include "SkXfermode.h"
+
+struct SkPoint;
+
+class SK_API SkLayerDrawLooper : public SkDrawLooper {
+public:
+            SkLayerDrawLooper();
+    virtual ~SkLayerDrawLooper();
+
+    /**
+     *  Bits specifies which aspects of the layer's paint should replace the
+     *  corresponding aspects on the draw's paint.
+     *  kEntirePaint_Bits means use the layer's paint completely.
+     *  0 means ignore the layer's paint... except that LayerInfo's fFlagsMask
+     *  and fColorMode are always applied.
+     */
+    enum Bits {
+        kStyle_Bit      = 1 << 0,   //!< use this layer's Style/stroke settings
+        kTextSkewX_Bit  = 1 << 1,   //!< use this layer's textskewx
+        kPathEffect_Bit = 1 << 2,   //!< use this layer's patheffect
+        kMaskFilter_Bit = 1 << 3,   //!< use this layer's maskfilter
+        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
+         *    used to interpret the text/len parameters in draw[Pos]Text.
+         *  - Flags and Color are always computed using the LayerInfo's
+         *    fFlagsMask and fColorMode.
+         */
+        kEntirePaint_Bits = -1,
+        
+    };
+    typedef int32_t BitFlags;
+
+    /**
+     *  Info for how to apply the layer's paint and offset.
+     *
+     *  fFlagsMask selects which flags in the layer's paint should be applied.
+     *      result = (draw-flags & ~fFlagsMask) | (layer-flags & fFlagsMask)
+     *  In the extreme:
+     *      If fFlagsMask is 0, we ignore all of the layer's flags
+     *      If fFlagsMask is -1, we use all of the layer's flags
+     *
+     *  fColorMode controls how we compute the final color for the layer:
+     *      The layer's paint's color is treated as the SRC
+     *      The draw's paint's color is treated as the DST
+     *      final-color = Mode(layers-color, draws-color);
+     *  Any SkXfermode::Mode will work. Two common choices are:
+     *      kSrc_Mode: to use the layer's color, ignoring the draw's
+     *      kDst_Mode: to just keep the draw's color, ignoring the layer's
+     */
+    struct SK_API LayerInfo {
+        uint32_t            fFlagsMask; // SkPaint::Flags
+        BitFlags            fPaintBits;
+        SkXfermode::Mode    fColorMode;
+        SkVector            fOffset;
+        bool                fPostTranslate; //!< applies to fOffset
+
+        /**
+         *  Initial the LayerInfo. Defaults to settings that will draw the
+         *  layer with no changes: e.g.
+         *      fPaintBits == 0
+         *      fColorMode == kDst_Mode
+         *      fOffset == (0, 0)
+         */
+        LayerInfo();
+    };
+
+    /**
+     *  Call for each layer you want to add (from top to bottom).
+     *  This returns a paint you can modify, but that ptr is only valid until
+     *  the next call made to addLayer().
+     */
+    SkPaint* addLayer(const LayerInfo&);
+
+    /**
+     *  This layer will draw with the original paint, ad 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()
+
+protected:
+    SkLayerDrawLooper(SkFlattenableReadBuffer&);
+
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+    
+private:
+    struct Rec {
+        Rec*    fNext;
+        SkPaint fPaint;
+        LayerInfo fInfo;
+
+        static Rec* Reverse(Rec*);
+    };
+    Rec*    fRecs;
+    int     fCount;
+
+    // state-machine during the init/next cycle
+    Rec* fCurrRec;
+
+    static void ApplyInfo(SkPaint* dst, const SkPaint& src, const LayerInfo&);
+
+    class MyRegistrar : public SkFlattenable::Registrar {
+    public:
+        MyRegistrar();
+    };
+    
+    typedef SkDrawLooper INHERITED;
+};
+
+
+#endif
diff --git a/legacy/include/effects/SkLayerRasterizer.h b/legacy/include/effects/SkLayerRasterizer.h
new file mode 100644
index 0000000..91deb61
--- /dev/null
+++ b/legacy/include/effects/SkLayerRasterizer.h
@@ -0,0 +1,57 @@
+
+/*
+ * 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 SkLayerRasterizer_DEFINED
+#define SkLayerRasterizer_DEFINED
+
+#include "SkRasterizer.h"
+#include "SkDeque.h"
+#include "SkScalar.h"
+
+class SkPaint;
+
+class 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.
+	*/
+    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()
+
+protected:
+    SkLayerRasterizer(SkFlattenableReadBuffer&);
+
+    // override from SkRasterizer
+    virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix,
+                             const SkIRect* clipBounds,
+                             SkMask* mask, SkMask::CreateMode mode);
+
+private:
+    SkDeque fLayers;
+
+    typedef SkRasterizer INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/SkMorphologyImageFilter.h b/legacy/include/effects/SkMorphologyImageFilter.h
new file mode 100644
index 0000000..2297938
--- /dev/null
+++ b/legacy/include/effects/SkMorphologyImageFilter.h
@@ -0,0 +1,65 @@
+/*
+ * 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 SkMorphologyImageFilter_DEFINED
+#define SkMorphologyImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+
+class SK_API SkMorphologyImageFilter : public SkImageFilter {
+public:
+    explicit SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer);
+    SkMorphologyImageFilter(int radiusX, int radiusY);
+
+protected:
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE;
+    SkISize    radius() const { return fRadius; }
+
+private:
+    SkISize    fRadius;
+    typedef SkImageFilter INHERITED;
+};
+
+class SK_API SkDilateImageFilter : public SkMorphologyImageFilter {
+public:
+    SkDilateImageFilter(int radiusX, int radiusY) : INHERITED(radiusX, radiusY) {}
+    explicit SkDilateImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    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()
+
+    typedef SkMorphologyImageFilter INHERITED;
+};
+
+class SK_API SkErodeImageFilter : public SkMorphologyImageFilter {
+public:
+    SkErodeImageFilter(int radiusX, int radiusY) : INHERITED(radiusX, radiusY) {}
+    explicit SkErodeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    virtual bool asAnErode(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(SkErodeImageFilter, (buffer));
+    }
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+private:
+    typedef SkMorphologyImageFilter INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkPaintFlagsDrawFilter.h b/legacy/include/effects/SkPaintFlagsDrawFilter.h
new file mode 100644
index 0000000..66a43cc
--- /dev/null
+++ b/legacy/include/effects/SkPaintFlagsDrawFilter.h
@@ -0,0 +1,29 @@
+
+/*
+ * 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 SkPaintFlagsDrawFilter_DEFINED
+#define SkPaintFlagsDrawFilter_DEFINED
+
+#include "SkDrawFilter.h"
+
+class SkPaintFlagsDrawFilter : public SkDrawFilter {
+public:
+    SkPaintFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags);
+    
+    // overrides
+    virtual void filter(SkPaint*, Type);
+    
+private:
+    uint32_t    fPrevFlags;     // local cache for filter/restore
+    uint16_t    fClearFlags;    // user specified
+    uint16_t    fSetFlags;      // user specified
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkPixelXorXfermode.h b/legacy/include/effects/SkPixelXorXfermode.h
new file mode 100644
index 0000000..a7197ab
--- /dev/null
+++ b/legacy/include/effects/SkPixelXorXfermode.h
@@ -0,0 +1,48 @@
+
+/*
+ * Copyright 2007 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 SkPixelXorXfermode_DEFINED
+#define SkPixelXorXfermode_DEFINED
+
+#include "SkXfermode.h"
+
+/** SkPixelXorXfermode implements a simple pixel xor (op ^ src ^ dst).
+    This transformation does not follow premultiplied conventions, therefore
+    this proc *always* returns an opaque color (alpha == 255). Thus it is
+    not really usefull for operating on blended colors.
+*/
+class 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()
+
+protected:
+    // override from SkXfermode
+    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst);
+
+private:
+    SkColor fOpColor;
+
+    SkPixelXorXfermode(SkFlattenableReadBuffer& rb);
+    // our private factory
+    static SkFlattenable* Create(SkFlattenableReadBuffer&);
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/SkPorterDuff.h b/legacy/include/effects/SkPorterDuff.h
new file mode 100644
index 0000000..44d94f8
--- /dev/null
+++ b/legacy/include/effects/SkPorterDuff.h
@@ -0,0 +1,84 @@
+
+/*
+ * 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 SkPorterDuff_DEFINED
+#define SkPorterDuff_DEFINED
+
+#include "SkColor.h"
+#include "SkXfermode.h"
+
+class SkXfermode;
+
+/** DEPRECATED - use SkXfermode::Mode instead
+ */
+class SkPorterDuff {
+public:
+    /** List of predefined xfermodes. In general, the algebra for the modes
+        uses the following symbols:
+        Sa, Sc  - source alpha and color
+        Da, Dc - destination alpha and color (before compositing)
+        [a, c] - Resulting (alpha, color) values
+        For these equations, the colors are in premultiplied state.
+        If no xfermode is specified, kSrcOver is assumed.
+    */
+    enum Mode {
+        kClear_Mode,    //!< [0, 0]
+        kSrc_Mode,      //!< [Sa, Sc]
+        kDst_Mode,      //!< [Da, Dc]
+        kSrcOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc]
+        kDstOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc]
+        kSrcIn_Mode,    //!< [Sa * Da, Sc * Da]
+        kDstIn_Mode,    //!< [Sa * Da, Sa * Dc]
+        kSrcOut_Mode,   //!< [Sa * (1 - Da), Sc * (1 - Da)]
+        kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+        kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+        kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+        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]
+        kScreen_Mode,   //!< [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
+        kAdd_Mode,      //!< Saturate(S + D)
+#ifdef SK_BUILD_FOR_ANDROID
+        kOverlay_Mode,
+#endif
+
+        kModeCount
+    };
+
+    /** 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,
+        return false and ignore the mode parameter.
+    */
+    static bool IsMode(SkXfermode*, Mode* mode);
+
+    /** Return the corersponding SkXfermode::Mode
+     */
+    static SkXfermode::Mode ToXfermodeMode(Mode);
+};
+
+#endif
+
diff --git a/include/effects/SkRectShape.h b/legacy/include/effects/SkRectShape.h
similarity index 100%
rename from include/effects/SkRectShape.h
rename to legacy/include/effects/SkRectShape.h
diff --git a/legacy/include/effects/SkTableColorFilter.h b/legacy/include/effects/SkTableColorFilter.h
new file mode 100644
index 0000000..b442197
--- /dev/null
+++ b/legacy/include/effects/SkTableColorFilter.h
@@ -0,0 +1,34 @@
+
+#ifndef SkTableColorFilter_DEFINED
+#define SkTableColorFilter_DEFINED
+
+#include "SkColorFilter.h"
+
+class SK_API SkTableColorFilter {
+public:
+    /**
+     *  Create a table colorfilter, copying the table into the filter, and
+     *  applying it to all 4 components.
+     *      a' = table[a];
+     *      r' = table[r];
+     *      g' = table[g];
+     *      b' = table[b];
+     *  Compoents are operated on in unpremultiplied space. If the incomming
+     *  colors are premultiplied, they are temporarily unpremultiplied, then
+     *  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
+     *  treated as identity, with the component left unchanged. If a table
+     *  is not null, then its contents are copied into the filter.
+     */
+    static SkColorFilter* CreateARGB(const uint8_t tableA[256],
+                                     const uint8_t tableR[256],
+                                     const uint8_t tableG[256],
+                                     const uint8_t tableB[256]);
+};
+
+#endif
diff --git a/legacy/include/effects/SkTableMaskFilter.h b/legacy/include/effects/SkTableMaskFilter.h
new file mode 100644
index 0000000..f213de7
--- /dev/null
+++ b/legacy/include/effects/SkTableMaskFilter.h
@@ -0,0 +1,69 @@
+
+/*
+ * 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 SkTableMaskFilter_DEFINED
+#define SkTableMaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+#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 {
+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);
+
+    /** Utility that creates a clipping table: clamps values below min to 0
+        and above max to 255, and rescales the remaining into 0..255
+     */
+    static void MakeClipTable(uint8_t table[256], uint8_t min, uint8_t max);
+
+    static SkTableMaskFilter* CreateGamma(SkScalar gamma) {
+        uint8_t table[256];
+        MakeGammaTable(table, gamma);
+        return SkNEW_ARGS(SkTableMaskFilter, (table));
+    }
+
+    static SkTableMaskFilter* CreateClip(uint8_t min, uint8_t max) {
+        uint8_t table[256];
+        MakeClipTable(table, min, max);
+        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();
+
+protected:
+    SkTableMaskFilter(SkFlattenableReadBuffer& rb);
+    static SkFlattenable* Factory(SkFlattenableReadBuffer&);
+
+private:
+    uint8_t fTable[256];
+    
+    typedef SkMaskFilter INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/effects/SkTestImageFilters.h b/legacy/include/effects/SkTestImageFilters.h
new file mode 100755
index 0000000..55522c1
--- /dev/null
+++ b/legacy/include/effects/SkTestImageFilters.h
@@ -0,0 +1,156 @@
+
+#ifndef _SkTestImageFilters_h
+#define _SkTestImageFilters_h
+
+#include "SkImageFilter.h"
+#include "SkColorFilter.h"
+
+class SkOffsetImageFilter : 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);
+    }
+    virtual ~SkComposeImageFilter();
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkComposeImageFilter, (buffer));
+    }
+    
+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 {
+public:
+    SkDownSampleImageFilter(SkScalar scale) : fScale(scale) {}
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkDownSampleImageFilter, (buffer));
+    }
+    
+protected:
+    SkDownSampleImageFilter(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:
+    SkScalar fScale;
+    
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
diff --git a/legacy/include/effects/SkTransparentShader.h b/legacy/include/effects/SkTransparentShader.h
new file mode 100644
index 0000000..e951bba
--- /dev/null
+++ b/legacy/include/effects/SkTransparentShader.h
@@ -0,0 +1,45 @@
+
+/*
+ * 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 SkTransparentShader_DEFINED
+#define SkTransparentShader_DEFINED
+
+#include "SkShader.h"
+
+class 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 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;
+
+private:
+    // these are a cache from the call to setContext()
+    const SkBitmap* fDevice;
+    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/legacy/include/images/SkBitmapRegionDecoder.h b/legacy/include/images/SkBitmapRegionDecoder.h
new file mode 100644
index 0000000..5c8df3e
--- /dev/null
+++ b/legacy/include/images/SkBitmapRegionDecoder.h
@@ -0,0 +1,46 @@
+/*
+ * 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 SkBitmapRegionDecoder_DEFINED
+#define SkBitmapRegionDecoder_DEFINED
+
+#include "SkBitmap.h"
+#include "SkRect.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+
+class SkBitmapRegionDecoder {
+public:
+    SkBitmapRegionDecoder(SkImageDecoder *decoder, SkStream *stream,
+            int width, int height) {
+        fDecoder = decoder;
+        fStream = stream;
+        fWidth = width;
+        fHeight = height;
+    }
+    virtual ~SkBitmapRegionDecoder() {
+        delete fDecoder;
+        fStream->unref();
+    }
+
+    virtual bool decodeRegion(SkBitmap* bitmap, SkIRect rect,
+                              SkBitmap::Config pref, int sampleSize);
+
+    virtual int getWidth() { return fWidth; }
+    virtual int getHeight() { return fHeight; }
+
+    virtual SkImageDecoder* getDecoder() { return fDecoder; }
+
+private:
+    SkImageDecoder *fDecoder;
+    SkStream *fStream;
+    int fWidth;
+    int fHeight;
+};
+
+#endif
diff --git a/legacy/include/images/SkFlipPixelRef.h b/legacy/include/images/SkFlipPixelRef.h
new file mode 100644
index 0000000..455a3da
--- /dev/null
+++ b/legacy/include/images/SkFlipPixelRef.h
@@ -0,0 +1,109 @@
+
+/*
+ * 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 SkFlipPixelRef_DEFINED
+#define SkFlipPixelRef_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPageFlipper.h"
+#include "SkPixelRef.h"
+#include "SkThread.h"
+
+class SkRegion;
+
+class SkFlipPixelRef : public SkPixelRef {
+public:
+            SkFlipPixelRef(SkBitmap::Config, int width, int height);
+    virtual ~SkFlipPixelRef();
+    
+    bool isDirty() const { return fFlipper.isDirty(); }
+    const SkRegion& dirtyRgn() const { return fFlipper.dirtyRgn(); }
+
+    void inval() { fFlipper.inval(); }
+    void inval(const SkIRect& rect) { fFlipper.inval(rect); }
+    void inval(const SkRegion& rgn) { fFlipper.inval(rgn); }
+    void inval(const SkRect& r, bool doAA) { fFlipper.inval(r, doAA); }
+
+    const SkRegion& beginUpdate(SkBitmap* device);
+    void endUpdate();
+    
+private:
+    void getFrontBack(const void** front, void** back) const {
+        if (front) {
+            *front = fPage0;
+        }
+        if (back) {
+            *back = fPage1;
+        }
+    }
+
+    void    swapPages();
+
+    // Helper to copy pixels from srcAddr to the dst bitmap, clipped to clip.
+    // srcAddr points to memory with the same config as dst.
+    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;
+    
+    void*           fStorage;
+    void*           fPage0; // points into fStorage;
+    void*           fPage1; // points into fStorage;
+    size_t          fSize;  // size of 1 page. fStorage holds 2 pages
+    SkBitmap::Config fConfig;
+
+    typedef SkPixelRef INHERITED;
+};
+
+class SkAutoFlipUpdate : SkNoncopyable {
+public:
+    SkAutoFlipUpdate(SkFlipPixelRef* ref) : fRef(ref) {
+        fDirty = &ref->beginUpdate(&fBitmap);
+    }
+    ~SkAutoFlipUpdate() {
+        if (fRef) {
+            fRef->endUpdate();
+        }
+    }
+    
+    const SkBitmap& bitmap() const { return fBitmap; }
+    const SkRegion& dirty() const { return *fDirty; }
+    
+    // optional. This gets automatically called in the destructor (only once)
+    void endUpdate() {
+        if (fRef) {
+            fRef->endUpdate();
+            fRef = NULL;
+        }
+    }
+
+private:
+    SkFlipPixelRef* fRef;
+    SkBitmap        fBitmap;
+    const SkRegion* fDirty;
+};
+
+#endif
diff --git a/legacy/include/images/SkImageDecoder.h b/legacy/include/images/SkImageDecoder.h
new file mode 100644
index 0000000..361e1a0
--- /dev/null
+++ b/legacy/include/images/SkImageDecoder.h
@@ -0,0 +1,427 @@
+
+/*
+ * 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 SkImageDecoder_DEFINED
+#define SkImageDecoder_DEFINED
+
+#include "SkBitmap.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+
+class SkStream;
+
+class SkVMMemoryReporter : public SkRefCnt {
+public:
+    virtual ~SkVMMemoryReporter();
+    virtual bool reportMemory(size_t memorySize) = 0;
+};
+
+/** \class SkImageDecoder
+
+    Base class for decoding compressed images into a SkBitmap
+*/
+class SkImageDecoder {
+public:
+    virtual ~SkImageDecoder();
+
+    // Should be consistent with kFormatName
+    enum Format {
+        kUnknown_Format,
+        kBMP_Format,
+        kGIF_Format,
+        kICO_Format,
+        kJPEG_Format,
+        kPNG_Format,
+        kWBMP_Format,
+        kWEBP_Format,
+
+        kLastKnownFormat = kWEBP_Format
+    };
+
+    /** Contains the image format name.
+     *  This should be consistent with Format.
+     *
+     *  The format name gives a more meaningful error message than enum.
+     */
+    static const char *kFormatName[8];
+
+    /** Return the compressed data's format (see Format enum)
+    */
+    virtual Format getFormat() const;
+
+    /** Return the compressed data's format name.
+    */
+    const char* getFormatName() const { return kFormatName[getFormat()]; }
+
+    /** Returns true if the decoder should try to dither the resulting image.
+        The default setting is true.
+    */
+    bool getDitherImage() const { return fDitherImage; }
+
+    /** Set to true if the the decoder should try to dither the resulting image.
+        The default setting is true.
+    */
+    void setDitherImage(bool dither) { fDitherImage = dither; }
+
+    /** Returns true if the decoder should try to decode the
+        resulting image to a higher quality even at the expense of
+        the decoding speed.
+    */
+    bool getPreferQualityOverSpeed() const { return fPreferQualityOverSpeed; }
+
+    /** Set to true if the the decoder should try to decode the
+        resulting image to a higher quality even at the expense of
+        the decoding speed.
+    */
+    void setPreferQualityOverSpeed(bool qualityOverSpeed) {
+        fPreferQualityOverSpeed = qualityOverSpeed;
+    }
+
+    /** \class Peeker
+
+        Base class for optional callbacks to retrieve meta/chunk data out of
+        an image as it is being decoded.
+    */
+    class Peeker : public SkRefCnt {
+    public:
+        /** 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;
+    };
+
+    Peeker* getPeeker() const { return fPeeker; }
+    Peeker* setPeeker(Peeker*);
+
+    /** \class Peeker
+
+        Base class for optional callbacks to retrieve meta/chunk data out of
+        an image as it is being decoded.
+    */
+    class Chooser : public SkRefCnt {
+    public:
+        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;
+    };
+
+    Chooser* getChooser() const { return fChooser; }
+    Chooser* setChooser(Chooser*);
+
+    /** This optional table describes the caller's preferred config based on
+        information about the src data. For this table, the src attributes are
+        described in terms of depth (index (8), 16, 32/24) and if there is
+        per-pixel alpha. These inputs combine to create an index into the
+        pref[] table, which contains the caller's preferred config for that
+        input, or kNo_Config if there is no preference.
+
+        To specify no preferrence, call setPrefConfigTable(NULL), which is
+        the default.
+
+        Note, it is still at the discretion of the codec as to what output
+        config is actually returned, as it may not be able to support the
+        caller's preference.
+
+        Here is how the index into the table is computed from the src:
+            depth [8, 16, 32/24] -> 0, 2, 4
+            alpha [no, yes] -> 0, 1
+        The two index values are OR'd together.
+            src: 8-index, no-alpha  -> 0
+            src: 8-index, yes-alpha -> 1
+            src: 16bit,   no-alpha  -> 2    // e.g. 565
+            src: 16bit,   yes-alpha -> 3    // e.g. 1555
+            src: 32/24,   no-alpha  -> 4
+            src: 32/24,   yes-alpha -> 5
+     */
+    void setPrefConfigTable(const SkBitmap::Config pref[6]);
+
+    SkBitmap::Allocator* getAllocator() const { return fAllocator; }
+    SkBitmap::Allocator* setAllocator(SkBitmap::Allocator*);
+    SkVMMemoryReporter* setReporter(SkVMMemoryReporter*);
+
+    // sample-size, if set to > 1, tells the decoder to return a smaller than
+    // original bitmap, sampling 1 pixel for every size pixels. e.g. if sample
+    // size is set to 3, then the returned bitmap will be 1/3 as wide and high,
+    // and will contain 1/9 as many pixels as the original.
+    // Note: this is a hint, and the codec may choose to ignore this, or only
+    // approximate the sample size.
+    int getSampleSize() const { return fSampleSize; }
+    void setSampleSize(int size);
+
+    /** Reset the sampleSize to its default of 1
+     */
+    void resetSampleSize() { this->setSampleSize(1); }
+
+    /** Decoding is synchronous, but for long decodes, a different thread can
+        call this method safely. This sets a state that the decoders will
+        periodically check, and if they see it changed to cancel, they will
+        cancel. This will result in decode() returning false. However, there is
+        no guarantee that the decoder will see the state change in time, so
+        it is possible that cancelDecode() will be called, but will be ignored
+        and decode() will return true (assuming no other problems were
+        encountered).
+
+        This state is automatically reset at the beginning of decode().
+     */
+    void cancelDecode() {
+        // now the subclass must query shouldCancelDecode() to be informed
+        // of the request
+        fShouldCancelDecode = true;
+    }
+
+    /** Passed to the decode method. If kDecodeBounds_Mode is passed, then
+        only the bitmap's width/height/config need be set. If kDecodePixels_Mode
+        is passed, then the bitmap must have pixels or a pixelRef.
+    */
+    enum Mode {
+        kDecodeBounds_Mode, //!< only return width/height/config in bitmap
+        kDecodePixels_Mode  //!< return entire bitmap (including pixels)
+    };
+
+    /** Given a stream, decode it into the specified bitmap.
+        If the decoder can decompress the image, it calls bitmap.setConfig(),
+        and then if the Mode is kDecodePixels_Mode, call allocPixelRef(),
+        which will allocated a pixelRef. To access the pixel memory, the codec
+        needs to call lockPixels/unlockPixels on the
+        bitmap. It can then set the pixels with the decompressed image.
+    *   If the image cannot be decompressed, return false. After the
+    *   decoding, the function converts the decoded config in bitmap
+    *   to pref if possible. Whether a conversion is feasible is
+    *   tested by Bitmap::canCopyTo(pref).
+
+        note: document use of Allocator, Peeker and Chooser
+    */
+    bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode, bool reuseBitmap = false);
+    bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode, bool reuseBitmap = false) {
+        return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode, reuseBitmap);
+    }
+
+    /**
+     * Given a stream, build an index for doing tile-based decode.
+     * The built index will be saved in the decoder, and the image size will
+     * be returned in width and height.
+     *
+     * Return true for success or false on failure.
+     */
+    virtual bool buildTileIndex(SkStream*,
+                                int *width, int *height);
+
+    /**
+     * Decode a rectangle region in the image specified by rect.
+     * The method can only be called after buildTileIndex().
+     *
+     * Return true for success.
+     * Return false if the index is never built or failing in decoding.
+     */
+    virtual bool decodeRegion(SkBitmap* bitmap, SkIRect rect,
+                              SkBitmap::Config pref);
+
+    /** Given a stream, this will try to find an appropriate decoder object.
+        If none is found, the method returns NULL.
+    */
+    static SkImageDecoder* Factory(SkStream*);
+
+    /** Decode the image stored in the specified file, and store the result
+        in bitmap. Return true for success or false on failure.
+
+        If pref is kNo_Config, then the decoder is free to choose the most natural
+        config given the image data. If pref something other than kNo_Config,
+        the decoder will attempt to decode the image into that format, unless
+        there is a conflict (e.g. the image has per-pixel alpha and the bitmap's
+        config does not support that), in which case the decoder will choose a
+        closest match configuration.
+
+        @param format On success, if format is non-null, it is set to the format
+                      of the decoded file. On failure it is ignored.
+    */
+    static bool DecodeFile(const char file[], SkBitmap* bitmap,
+                           SkBitmap::Config prefConfig, Mode,
+                           Format* format = NULL);
+    static bool DecodeFile(const char file[], SkBitmap* bitmap) {
+        return DecodeFile(file, bitmap, SkBitmap::kNo_Config,
+                          kDecodePixels_Mode, NULL);
+    }
+    /** Decode the image stored in the specified memory buffer, and store the
+        result in bitmap. Return true for success or false on failure.
+
+        If pref is kNo_Config, then the decoder is free to choose the most natural
+        config given the image data. If pref something other than kNo_Config,
+        the decoder will attempt to decode the image into that format, unless
+        there is a conflict (e.g. the image has per-pixel alpha and the bitmap's
+        config does not support that), in which case the decoder will choose a
+        closest match configuration.
+
+        @param format On success, if format is non-null, it is set to the format
+                       of the decoded buffer. On failure it is ignored.
+     */
+    static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap,
+                             SkBitmap::Config prefConfig, Mode,
+                             Format* format = NULL);
+    static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap){
+        return DecodeMemory(buffer, size, bitmap, SkBitmap::kNo_Config,
+                            kDecodePixels_Mode, NULL);
+    }
+    /** Decode the image stored in the specified SkStream, and store the result
+        in bitmap. Return true for success or false on failure.
+
+        If pref is kNo_Config, then the decoder is free to choose the most
+        natural config given the image data. If pref something other than
+        kNo_Config, the decoder will attempt to decode the image into that
+        format, unless there is a conflict (e.g. the image has per-pixel alpha
+        and the bitmap's config does not support that), in which case the
+        decoder will choose a closest match configuration.
+
+        @param format On success, if format is non-null, it is set to the format
+                      of the decoded stream. On failure it is ignored.
+     */
+    static bool DecodeStream(SkStream* stream, SkBitmap* bitmap,
+                             SkBitmap::Config prefConfig, Mode,
+                             Format* format = NULL);
+    static bool DecodeStream(SkStream* stream, SkBitmap* bitmap) {
+        return DecodeStream(stream, bitmap, SkBitmap::kNo_Config,
+                            kDecodePixels_Mode, NULL);
+    }
+
+    /** Return the default config for the running device.
+        Currently this used as a suggestion to image decoders that need to guess
+        what config they should decode into.
+        Default is kNo_Config, but this can be changed with SetDeviceConfig()
+    */
+    static SkBitmap::Config GetDeviceConfig();
+    /** Set the default config for the running device.
+        Currently this used as a suggestion to image decoders that need to guess
+        what config they should decode into.
+        Default is kNo_Config.
+        This can be queried with GetDeviceConfig()
+    */
+    static void SetDeviceConfig(SkBitmap::Config);
+
+  /** @cond UNIT_TEST */
+    SkDEBUGCODE(static void UnitTest();)
+  /** @endcond */
+
+protected:
+    // must be overridden in subclasses. This guy is called by decode(...)
+    virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;
+
+    // If the decoder wants to support tiled based decoding,
+    // this method must be overridden. This guy is called by buildTileIndex(...)
+    virtual bool onBuildTileIndex(SkStream*,
+                int *width, int *height) {
+        return false;
+    }
+
+    // If the decoder wants to support tiled based decoding,
+    // this method must be overridden. This guy is called by decodeRegion(...)
+    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect) {
+        return false;
+    }
+
+    /*
+     * Crop a rectangle from the src Bitmap to the dest Bitmap. src and dest are
+     * both sampled by sampleSize from an original Bitmap.
+     *
+     * @param dest the destination Bitmap.
+     * @param src the source Bitmap that is sampled by sampleSize from the original
+     *            Bitmap.
+     * @param sampleSize the sample size that src is sampled from the original Bitmap.
+     * @param (srcX, srcY) the upper-left point of the src Btimap in terms of
+     *                     the coordinate in the original Bitmap.
+     * @param (width, height) the width and height of the unsampled dest.
+     * @param (destX, destY) the upper-left point of the dest Bitmap in terms of
+     *                       the coordinate in the original Bitmap.
+     */
+    virtual void cropBitmap(SkBitmap *dest, SkBitmap *src, int sampleSize,
+                            int destX, int destY, int width, int height,
+                            int srcX, int srcY);
+
+
+
+    /** Can be queried from within onDecode, to see if the user (possibly in
+        a different thread) has requested the decode to cancel. If this returns
+        true, your onDecode() should stop and return false.
+        Each subclass needs to decide how often it can query this, to balance
+        responsiveness with performance.
+
+        Calling this outside of onDecode() may return undefined values.
+     */
+
+public:
+    bool shouldCancelDecode() const { return fShouldCancelDecode; }
+
+protected:
+    SkImageDecoder();
+
+    // helper function for decoders to handle the (common) case where there is only
+    // once choice available in the image file.
+    bool chooseFromOneChoice(SkBitmap::Config config, int width, int height) const;
+
+    /*  Helper for subclasses. Call this to allocate the pixel memory given the bitmap's
+        width/height/rowbytes/config. Returns true on success. This method handles checking
+        for an optional Allocator.
+    */
+    bool allocPixelRef(SkBitmap*, SkColorTable*) const;
+
+    enum SrcDepth {
+        kIndex_SrcDepth,
+        k16Bit_SrcDepth,
+        k32Bit_SrcDepth
+    };
+    /** The subclass, inside onDecode(), calls this to determine the config of
+        the returned bitmap. SrcDepth and hasAlpha reflect the raw data of the
+        src image. This routine returns the caller's preference given
+        srcDepth and hasAlpha, or kNo_Config if there is no preference.
+
+        Note: this also takes into account GetDeviceConfig(), so the subclass
+        need not call that.
+     */
+    SkBitmap::Config getPrefConfig(SrcDepth, bool hasAlpha) const;
+
+    SkVMMemoryReporter*      fReporter;
+
+private:
+    Peeker*                 fPeeker;
+    Chooser*                fChooser;
+    SkBitmap::Allocator*    fAllocator;
+    int                     fSampleSize;
+    SkBitmap::Config        fDefaultPref;   // use if fUsePrefTable is false
+    SkBitmap::Config        fPrefTable[6];  // use if fUsePrefTable is true
+    bool                    fDitherImage;
+    bool                    fUsePrefTable;
+    mutable bool            fShouldCancelDecode;
+    bool                    fPreferQualityOverSpeed;
+
+    // illegal
+    SkImageDecoder(const SkImageDecoder&);
+    SkImageDecoder& operator=(const SkImageDecoder&);
+};
+
+/** Calling newDecoder with a stream returns a new matching imagedecoder
+    instance, or NULL if none can be found. The caller must manage its ownership
+    of the stream as usual, calling unref() when it is done, as the returned
+    decoder may have called ref() (and if so, the decoder is responsible for
+    balancing its ownership when it is destroyed).
+ */
+class SkImageDecoderFactory : public SkRefCnt {
+public:
+    virtual SkImageDecoder* newDecoder(SkStream*) = 0;
+};
+
+class SkDefaultImageDecoderFactory : SkImageDecoderFactory {
+public:
+    // calls SkImageDecoder::Factory(stream)
+    virtual SkImageDecoder* newDecoder(SkStream* stream) {
+        return SkImageDecoder::Factory(stream);
+    }
+};
+
+
+#endif
diff --git a/legacy/include/images/SkImageEncoder.h b/legacy/include/images/SkImageEncoder.h
new file mode 100644
index 0000000..a4831f1
--- /dev/null
+++ b/legacy/include/images/SkImageEncoder.h
@@ -0,0 +1,44 @@
+
+/*
+ * 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 SkImageEncoder_DEFINED
+#define SkImageEncoder_DEFINED
+
+#include "SkTypes.h"
+
+class SkBitmap;
+class SkWStream;
+
+class SkImageEncoder {
+public:
+    enum Type {
+        kJPEG_Type,
+        kPNG_Type,
+        kWEBP_Type
+    };
+    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);
+
+    static bool EncodeFile(const char file[], const SkBitmap&, Type,
+                           int quality);
+    static bool EncodeStream(SkWStream*, const SkBitmap&, Type,
+                           int quality);
+
+protected:
+    virtual bool onEncode(SkWStream*, const SkBitmap&, int quality) = 0;
+};
+
+#endif
diff --git a/legacy/include/images/SkImageRef.h b/legacy/include/images/SkImageRef.h
new file mode 100644
index 0000000..f0f06b6
--- /dev/null
+++ b/legacy/include/images/SkImageRef.h
@@ -0,0 +1,106 @@
+
+/*
+ * 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 SkImageRef_DEFINED
+#define SkImageRef_DEFINED
+
+#include "SkPixelRef.h"
+#include "SkBitmap.h"
+#include "SkImageDecoder.h"
+#include "SkString.h"
+
+class SkImageRefPool;
+class SkStream;
+
+// define this to enable dumping whenever we add/remove/purge an imageref
+//#define DUMP_IMAGEREF_LIFECYCLE
+
+class SkImageRef : public SkPixelRef {
+public:
+    /** Create a new imageref from a stream. NOTE: the stream is not copied, but
+        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);
+    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!
+    */
+    virtual bool onDecode(SkImageDecoder* codec, SkStream*, SkBitmap*,
+                          SkBitmap::Config, SkImageDecoder::Mode);
+
+    /*  Overrides from SkPixelRef
+        When these are called, we will have already acquired the mutex!
+     */
+
+    virtual void* onLockPixels(SkColorTable**);
+    // override this in your subclass to clean up when we're unlocking pixels
+    virtual void onUnlockPixels();
+    
+    SkImageRef(SkFlattenableReadBuffer&);
+
+    SkBitmap fBitmap;
+
+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)
+    bool prepareBitmap(SkImageDecoder::Mode);
+
+    SkImageDecoderFactory*  fFactory;    // may be null
+    SkStream*               fStream;
+    SkBitmap::Config        fConfig;
+    int                     fSampleSize;
+    bool                    fDoDither;
+    bool                    fErrorInDecoding;
+    
+    friend class SkImageRefPool;
+    
+    SkImageRef*  fPrev, *fNext;    
+    size_t ramUsed() const;
+    
+    typedef SkPixelRef INHERITED;
+};
+
+#endif
diff --git a/legacy/include/images/SkImageRef_GlobalPool.h b/legacy/include/images/SkImageRef_GlobalPool.h
new file mode 100644
index 0000000..909ee71
--- /dev/null
+++ b/legacy/include/images/SkImageRef_GlobalPool.h
@@ -0,0 +1,67 @@
+
+/*
+ * 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 SkImageRef_GlobalPool_DEFINED
+#define SkImageRef_GlobalPool_DEFINED
+
+#include "SkImageRef.h"
+
+class SkImageRef_GlobalPool : public SkImageRef {
+public:
+    // 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()
+
+    // 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 <=
+     to the requested amount. In addition, because of the
+     chunky nature of the cache, the resulting usage may be < the requested
+     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:
+    typedef SkImageRef INHERITED;
+};
+
+#endif
diff --git a/legacy/include/images/SkJpegUtility.h b/legacy/include/images/SkJpegUtility.h
new file mode 100644
index 0000000..74f1a21
--- /dev/null
+++ b/legacy/include/images/SkJpegUtility.h
@@ -0,0 +1,66 @@
+
+/*
+ * 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 SkJpegUtility_DEFINED
+#define SkJpegUtility_DEFINED
+
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+#include <setjmp.h>
+
+/* Our error-handling struct.
+ *
+*/
+struct skjpeg_error_mgr : jpeg_error_mgr {
+    jmp_buf fJmpBuf;
+};
+
+
+void skjpeg_error_exit(j_common_ptr cinfo);
+
+///////////////////////////////////////////////////////////////////////////
+/* Our source struct for directing jpeg to our stream object.
+*/
+struct skjpeg_source_mgr : jpeg_source_mgr {
+    skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder, bool ownStream);
+    ~skjpeg_source_mgr();
+
+    SkStream*   fStream;
+    void*       fMemoryBase;
+    size_t      fMemoryBaseSize;
+    bool        fUnrefStream;
+    SkImageDecoder* fDecoder;
+    enum {
+        kBufferSize = 1024
+    };
+    char    fBuffer[kBufferSize];
+};
+
+/////////////////////////////////////////////////////////////////////////////
+/* Our destination struct for directing decompressed pixels to our stream
+ * object.
+ */
+struct skjpeg_destination_mgr : jpeg_destination_mgr {
+    skjpeg_destination_mgr(SkWStream* stream);
+
+    SkWStream*  fStream;
+
+    enum {
+        kBufferSize = 1024
+    };
+    uint8_t fBuffer[kBufferSize];
+};
+
+#endif
diff --git a/legacy/include/images/SkMovie.h b/legacy/include/images/SkMovie.h
new file mode 100644
index 0000000..f52a786
--- /dev/null
+++ b/legacy/include/images/SkMovie.h
@@ -0,0 +1,76 @@
+
+/*
+ * 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 SkMovie_DEFINED
+#define SkMovie_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkCanvas.h"
+
+class SkStream;
+
+class SkMovie : public SkRefCnt {
+public:
+    /** Try to create a movie from the stream. If the stream format is not
+        supported, return NULL.
+    */
+    static SkMovie* DecodeStream(SkStream*);
+    /** Try to create a movie from the specified file path. If the file is not
+        found, or the format is not supported, return NULL. If a movie is
+        returned, the stream may be retained by the movie (via ref()) until
+        the movie is finished with it (by calling unref()).
+    */
+    static SkMovie* DecodeFile(const char path[]);
+    /** Try to create a movie from the specified memory.
+        If the format is not supported, return NULL. If a movie is returned,
+        the data will have been read or copied, and so the caller may free
+        it.
+    */
+    static SkMovie* DecodeMemory(const void* data, size_t length);
+
+    SkMSec  duration();
+    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
+        redraw).
+    */
+    bool setTime(SkMSec);
+
+    // return the right bitmap for the current time code
+    const SkBitmap& bitmap();
+    
+protected:
+    struct Info {
+        SkMSec  fDuration;
+        int     fWidth;
+        int     fHeight;
+        bool    fIsOpaque;
+    };
+
+    virtual bool onGetInfo(Info*) = 0;
+    virtual bool onSetTime(SkMSec) = 0;
+    virtual bool onGetBitmap(SkBitmap*) = 0;
+
+    // visible for subclasses
+    SkMovie();
+
+private:
+    Info        fInfo;
+    SkMSec      fCurrTime;
+    SkBitmap    fBitmap;
+    bool        fNeedBitmap;
+    
+    void ensureInfo();
+};
+
+#endif
diff --git a/legacy/include/images/SkPageFlipper.h b/legacy/include/images/SkPageFlipper.h
new file mode 100644
index 0000000..1a18856
--- /dev/null
+++ b/legacy/include/images/SkPageFlipper.h
@@ -0,0 +1,63 @@
+
+/*
+ * 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 SkPageFlipper_DEFINED
+#define SkPageFlipper_DEFINED
+
+#include "SkRegion.h"
+
+/** SkPageFlipper manages alternating inval/dirty regions for a rectangular area
+    (like a bitmap). You call inval() to accumulate inval areas, and then when
+    you're ready to "flip" pages (i.e. draw into the one you've been
+    invalidating) you call update, which swaps the inval regions, and returns
+    two things to you: 1) the final inval region to be drawn into, and 2) the
+    region of pixels that should be copied from the "front" page onto the one
+    you're about to draw into. This copyBits region will be disjoint from the
+    inval region, so both need to be handled.
+ */
+class SkPageFlipper {
+public:
+    SkPageFlipper();
+    SkPageFlipper(int width, int height);
+    
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+
+    void resize(int width, int height);
+
+    bool isDirty() const { return !fDirty1->isEmpty(); }
+    const SkRegion& dirtyRgn() const { return *fDirty1; }
+
+    void inval();
+    void inval(const SkIRect&);
+    void inval(const SkRegion&);
+    void inval(const SkRect&, bool antialias);
+
+    /** When you're ready to write to the back page, call update. The returned
+        region is the invalidate are that needs to be drawn to. The copyBits
+        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.
+     */
+    const SkRegion& update(SkRegion* copyBits);
+
+private:
+    SkRegion*   fDirty0;
+    SkRegion*   fDirty1;
+    SkRegion    fDirty0Storage;
+    SkRegion    fDirty1Storage;
+    int         fWidth;
+    int         fHeight;
+};
+
+#endif
+
diff --git a/legacy/include/pipe/SkGPipe.h b/legacy/include/pipe/SkGPipe.h
new file mode 100644
index 0000000..29b058e
--- /dev/null
+++ b/legacy/include/pipe/SkGPipe.h
@@ -0,0 +1,95 @@
+
+/*
+ * 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 SkGPipe_DEFINED
+#define SkGPipe_DEFINED
+
+#include "SkWriter32.h"
+#include "SkFlattenable.h"
+
+class SkCanvas;
+
+// XLib.h might have defined Status already (ugh)
+#ifdef Status
+    #undef Status
+#endif
+
+class SkGPipeReader {
+public:
+    SkGPipeReader(SkCanvas* target);
+    ~SkGPipeReader();
+
+    enum Status {
+        kDone_Status,   //!< no more data expected from reader
+        kEOF_Status,    //!< need more data from reader
+        kError_Status,  //!< encountered error
+        kReadAtom_Status//!< finished reading an atom
+    };
+
+    // 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);
+private:
+    SkCanvas*           fCanvas;
+    class SkGPipeState* fState;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeController {
+public:
+    /**
+     *  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
+     *  actual >= minRequest. If NULL is returned, then actual is ignored and
+     *  writing will stop.
+     *
+     *  The returned block must be 4-byte aligned, and actual must be a
+     *  multiple of 4.
+     *  minRequest will always be a multiple of 4.
+     */
+    virtual void* requestBlock(size_t minRequest, size_t* actual) = 0;
+
+    /**
+     *  This is called each time some atomic portion of the data has been
+     *  written to the block (most recently returned by requestBlock()).
+     *  If bytes == 0, then the writer has finished.
+     *
+     *  bytes will always be a multiple of 4.
+     */
+    virtual void notifyWritten(size_t bytes) = 0;
+};
+
+class SkGPipeWriter {
+public:
+    SkGPipeWriter();
+    ~SkGPipeWriter();
+
+    bool isRecording() const { return NULL != fCanvas; }
+
+    enum Flags {
+        kCrossProcess_Flag = 1 << 0,
+    };
+
+    SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0);
+
+    // 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();
+
+private:
+    class SkGPipeCanvas* fCanvas;
+    SkGPipeController*   fController;
+    SkFactorySet         fFactorySet;
+    SkWriter32 fWriter;
+};
+
+#endif
diff --git a/legacy/include/ports/SkHarfBuzzFont.h b/legacy/include/ports/SkHarfBuzzFont.h
new file mode 100644
index 0000000..66c5534
--- /dev/null
+++ b/legacy/include/ports/SkHarfBuzzFont.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkHarfBuzzFont_DEFINED
+#define SkHarfBuzzFont_DEFINED
+
+extern "C" {
+#include "harfbuzz-shaper.h"
+//#include "harfbuzz-unicode.h"
+}
+
+#include "SkTypes.h"
+
+class SkPaint;
+class SkTypeface;
+
+class SkHarfBuzzFont {
+public:
+    /** The subclass returns the typeface for this font, or NULL
+     */
+    virtual SkTypeface* getTypeface() const = 0;
+    /** The subclass sets the text related attributes of the paint.
+        e.g. textSize, typeface, textSkewX, etc.
+        All of the attributes that could effect how the text is measured.
+        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.
+     */
+    static HB_Error GetFontTableFunc(void* skharfbuzzfont, const HB_Tag tag,
+                                     HB_Byte* buffer, HB_UInt* len);
+
+    static const HB_FontClass& GetFontClass();
+};
+
+#endif
diff --git a/include/ports/SkStream_Win.h b/legacy/include/ports/SkStream_Win.h
similarity index 100%
rename from include/ports/SkStream_Win.h
rename to legacy/include/ports/SkStream_Win.h
diff --git a/legacy/include/ports/SkTypeface_android.h b/legacy/include/ports/SkTypeface_android.h
new file mode 100644
index 0000000..c3eb3d1
--- /dev/null
+++ b/legacy/include/ports/SkTypeface_android.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 SkTypeface_android_DEFINED
+#define SkTypeface_android_DEFINED
+
+#include "SkTypeface.h"
+#include "SkPaint.h"
+
+#include "../harfbuzz/src/harfbuzz-shaper.h"
+
+/**
+ *  Return a new typeface for a fallback script. If the script is
+ *  not valid, or can not map to a font, returns null.
+ *  @param  script   The harfbuzz script id.
+ *  @param  style    The font style, for example bold
+ *  @param  elegant  true if we want the web friendly elegant version of the font
+ *  @return          reference to the matching typeface. Caller must call
+ *                   unref() when they are done.
+ */
+SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
+        SkPaint::FontVariant fontVariant = SkPaint::kDefault_Variant);
+
+#endif
diff --git a/legacy/include/ports/SkTypeface_mac.h b/legacy/include/ports/SkTypeface_mac.h
new file mode 100644
index 0000000..eb9d25f
--- /dev/null
+++ b/legacy/include/ports/SkTypeface_mac.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef SkTypeface_mac_DEFINED
+#define SkTypeface_mac_DEFINED
+
+#include "SkTypeface.h"
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#endif
+/**
+ *  Like the other Typeface create methods, this returns a new reference to the
+ *  corresponding typeface for the specified CTFontRef. The caller must call
+ *  unref() when it is finished.
+ */
+SK_API extern SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef);
+
+#endif
+
diff --git a/legacy/include/ports/SkTypeface_win.h b/legacy/include/ports/SkTypeface_win.h
new file mode 100644
index 0000000..6da843f
--- /dev/null
+++ b/legacy/include/ports/SkTypeface_win.h
@@ -0,0 +1,32 @@
+
+/*
+ * 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 SkTypeface_win_DEFINED
+#define SkTypeface_win_DEFINED
+
+#include "SkTypeface.h"
+
+/**
+ *  Like the other Typeface create methods, this returns a new reference to the
+ *  corresponding typeface for the specified logfont. The caller is responsible
+ *  for calling unref() when it is finished.
+ */
+SK_API SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&);
+
+/**
+ *  Copy the LOGFONT associated with this typeface into the lf parameter. Note
+ *  that the lfHeight will need to be set afterwards, since the typeface does
+ *  not track this (the paint does).
+ *  typeface may be NULL, in which case we return the logfont for the default font.
+ */
+SK_API void SkLOGFONTFromTypeface(const SkTypeface* typeface, LOGFONT* lf);
+
+#endif
+
diff --git a/legacy/include/text/SkTextLayout.h b/legacy/include/text/SkTextLayout.h
new file mode 100644
index 0000000..e1e3e56
--- /dev/null
+++ b/legacy/include/text/SkTextLayout.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 SkTextLayout_DEFINED
+#define SkTextLayout_DEFINED
+
+#include "SkPaint.h"
+#include "SkRefCnt.h"
+
+class SkTextStyle : public SkRefCnt {
+public:
+    SkTextStyle();
+    SkTextStyle(const SkTextStyle&);
+    explicit SkTextStyle(const SkPaint&);
+    virtual ~SkTextStyle();
+
+    const SkPaint& paint() const { return fPaint; }
+    SkPaint& paint() { return fPaint; }
+    
+    // todo: bidi-override, language
+
+private:
+    SkPaint fPaint;
+};
+
+class SkTextLayout {
+public:
+    SkTextLayout();
+    ~SkTextLayout();
+
+    void setText(const char text[], size_t length);
+    void setBounds(const SkRect& bounds);
+
+    SkTextStyle* getDefaultStyle() const { return fDefaultStyle; }
+    SkTextStyle* setDefaultStyle(SkTextStyle*);
+
+//    SkTextStyle* setStyle(SkTextStyle*, size_t offset, size_t length);
+
+    void draw(SkCanvas* canvas);
+
+private:
+    SkTDArray<char> fText;
+    SkTextStyle*    fDefaultStyle;
+    SkRect          fBounds;
+
+    // cache
+    struct Line;
+    struct GlyphRun;
+    SkTDArray<Line*> fLines;
+};
+
+#endif
+
diff --git a/legacy/include/utils/SkBoundaryPatch.h b/legacy/include/utils/SkBoundaryPatch.h
new file mode 100644
index 0000000..9d4b5ad
--- /dev/null
+++ b/legacy/include/utils/SkBoundaryPatch.h
@@ -0,0 +1,62 @@
+
+/*
+ * 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 SkBoundaryPatch_DEFINED
+#define SkBoundaryPatch_DEFINED
+
+#include "SkPoint.h"
+#include "SkRefCnt.h"
+
+class SkBoundary : public SkRefCnt {
+public:
+    // These must be 0, 1, 2, 3 for efficiency in the subclass implementations
+    enum Edge {
+        kTop    = 0,
+        kRight  = 1,
+        kBottom = 2,
+        kLeft   = 3
+    };
+    // Edge index goes clockwise around the boundary, beginning at the "top"
+    virtual SkPoint eval(Edge, SkScalar unitInterval) = 0;
+};
+
+class SkBoundaryPatch {
+public:
+    SkBoundaryPatch();
+    ~SkBoundaryPatch();
+
+    SkBoundary* getBoundary() const { return fBoundary; }
+    SkBoundary* setBoundary(SkBoundary*);
+
+    SkPoint eval(SkScalar unitU, SkScalar unitV);
+    bool evalPatch(SkPoint verts[], int rows, int cols);
+
+private:
+    SkBoundary* fBoundary;
+};
+
+////////////////////////////////////////////////////////////////////////
+
+class SkLineBoundary : public SkBoundary {
+public:
+    SkPoint fPts[4];
+    
+    // override
+    virtual SkPoint eval(Edge, SkScalar);
+};
+
+class SkCubicBoundary : public SkBoundary {
+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/legacy/include/utils/SkCamera.h b/legacy/include/utils/SkCamera.h
new file mode 100644
index 0000000..1c4c1fb
--- /dev/null
+++ b/legacy/include/utils/SkCamera.h
@@ -0,0 +1,176 @@
+
+/*
+ * 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.
+ */
+
+
+
+
+//  Inspired by Rob Johnson's most excellent QuickDraw GX sample code
+
+#ifndef SkCamera_DEFINED
+#define SkCamera_DEFINED
+
+#include "Sk64.h"
+#include "SkMatrix.h"
+
+class SkCanvas;
+
+#ifdef SK_SCALAR_IS_FIXED
+    typedef SkFract SkUnitScalar;
+    #define SK_UnitScalar1          SK_Fract1
+    #define SkUnitScalarMul(a, b)   SkFractMul(a, b)
+    #define SkUnitScalarDiv(a, b)   SkFractDiv(a, b)
+#else
+    typedef float   SkUnitScalar;
+    #define SK_UnitScalar1          SK_Scalar1
+    #define SkUnitScalarMul(a, b)   SkScalarMul(a, b)
+    #define SkUnitScalarDiv(a, b)   SkScalarDiv(a, b)
+#endif
+
+struct SkUnit3D {
+    SkUnitScalar    fX, fY, fZ;
+
+    void set(SkUnitScalar x, SkUnitScalar y, SkUnitScalar z)
+    {
+        fX = x; fY = y; fZ = z;
+    }
+    static SkUnitScalar Dot(const SkUnit3D&, const SkUnit3D&);
+    static void Cross(const SkUnit3D&, const SkUnit3D&, SkUnit3D* cross);
+};
+
+struct SkPoint3D {
+    SkScalar    fX, fY, fZ;
+
+    void set(SkScalar x, SkScalar y, SkScalar z)
+    {
+        fX = x; fY = y; fZ = z;
+    }
+    SkScalar    normalize(SkUnit3D*) const;
+};
+typedef SkPoint3D SkVector3D;
+
+struct SkMatrix3D {
+    SkScalar    fMat[3][4];
+    
+    void reset();
+
+    void setRow(int row, SkScalar a, SkScalar b, SkScalar c, SkScalar d = 0)
+    {
+        SkASSERT((unsigned)row < 3);
+        fMat[row][0] = a;
+        fMat[row][1] = b;
+        fMat[row][2] = c;
+        fMat[row][3] = d;
+    }
+
+    void setRotateX(SkScalar deg);
+    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);
+    void preTranslate(SkScalar x, SkScalar y, SkScalar z);
+
+    void setConcat(const SkMatrix3D& a, const SkMatrix3D& b);
+    void mapPoint(const SkPoint3D& src, SkPoint3D* dst) const;
+    void mapVector(const SkVector3D& src, SkVector3D* dst) const;
+
+    void mapPoint(SkPoint3D* v) const
+    {
+        this->mapPoint(*v, v);
+    }
+    void mapVector(SkVector3D* v) const
+    {
+        this->mapVector(*v, v);
+    }
+};
+
+class SkPatch3D {
+public:
+    SkPatch3D();
+
+    void    reset();
+    void    transform(const SkMatrix3D&, SkPatch3D* dst = NULL) const;
+
+    // dot a unit vector with the patch's normal
+    SkScalar dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const;
+    SkScalar dotWith(const SkVector3D& v) const
+    {
+        return this->dotWith(v.fX, v.fY, v.fZ);
+    }
+
+    // depreicated, but still here for animator (for now)
+    void rotate(SkScalar x, SkScalar y, SkScalar z) {}
+    void rotateDegrees(SkScalar x, SkScalar y, SkScalar z) {}
+
+private:
+public: // make public for SkDraw3D for now
+    SkVector3D  fU, fV;
+    SkPoint3D   fOrigin;
+    
+    friend class SkCamera3D;
+};
+
+class SkCamera3D {
+public:
+    SkCamera3D();
+
+    void reset();
+    void update();
+    void patchToMatrix(const SkPatch3D&, SkMatrix* matrix) const;
+
+    SkPoint3D   fLocation;
+    SkPoint3D   fAxis;
+    SkPoint3D   fZenith;
+    SkPoint3D   fObserver;
+
+private:
+    mutable SkMatrix    fOrientation;
+    mutable bool        fNeedToUpdate;
+
+    void doUpdate() const;
+};
+
+class Sk3DView : SkNoncopyable {
+public:
+    Sk3DView();
+    ~Sk3DView();
+
+    void save();
+    void restore();
+
+    void translate(SkScalar x, SkScalar y, SkScalar z);
+    void rotateX(SkScalar deg);
+    void rotateY(SkScalar deg);
+    void rotateZ(SkScalar deg);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    void setCameraLocation(SkScalar x, SkScalar y, SkScalar z);
+    SkScalar getCameraLocationX();
+    SkScalar getCameraLocationY();
+    SkScalar getCameraLocationZ();
+#endif
+
+    void getMatrix(SkMatrix*) const;
+    void applyToCanvas(SkCanvas*) const;
+
+    SkScalar dotWithNormal(SkScalar dx, SkScalar dy, SkScalar dz) const;
+    
+private:
+    struct Rec {
+        Rec*        fNext;
+        SkMatrix3D  fMatrix;
+    };
+    Rec*        fRec;
+    Rec         fInitialRec;
+    SkCamera3D  fCamera;
+};
+
+#endif
+
diff --git a/legacy/include/utils/SkCubicInterval.h b/legacy/include/utils/SkCubicInterval.h
new file mode 100644
index 0000000..bd1f9b9
--- /dev/null
+++ b/legacy/include/utils/SkCubicInterval.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 SkCubicInterval_DEFINED
+#define SkCubicInterval_DEFINED
+
+#include "SkPoint.h"
+
+SkScalar SkEvalCubicInterval(SkScalar x1, SkScalar y1,
+                             SkScalar x2, SkScalar y2,
+                             SkScalar unitX);
+
+static inline SkScalar SkEvalCubicInterval(const SkPoint pts[2], SkScalar x) {
+    return SkEvalCubicInterval(pts[0].fX, pts[0].fY,
+                               pts[1].fX, pts[1].fY, x);
+}
+
+#endif
+
diff --git a/legacy/include/utils/SkCullPoints.h b/legacy/include/utils/SkCullPoints.h
new file mode 100644
index 0000000..9e2c01a
--- /dev/null
+++ b/legacy/include/utils/SkCullPoints.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 SkCullPoints_DEFINED
+#define SkCullPoints_DEFINED
+
+#include "SkRect.h"
+
+class SkCullPoints {
+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]);
+        kMoveToLineTo_Result    //!< path.moveTo(pts[0]); path.lineTo(pts[1]);
+    };
+    /** Connect a line to the previous call to lineTo (or moveTo).
+    */
+    LineToResult lineTo(int x, int y, SkIPoint pts[2]);
+
+private:
+    SkIRect      fR;             // the caller's rectangle
+    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;
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+class SkPath;
+
+/** \class SkCullPointsPath
+
+    Similar to SkCullPoints, but this class handles the return values
+    from lineTo, and automatically builds a SkPath with the result(s).
+*/
+class SkCullPointsPath {
+public:
+    SkCullPointsPath();
+    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);
+
+private:
+    SkCullPoints    fCP;
+    SkPath*         fPath;
+};
+
+#endif
diff --git a/legacy/include/utils/SkDeferredCanvas.h b/legacy/include/utils/SkDeferredCanvas.h
new file mode 100644
index 0000000..87797ac
--- /dev/null
+++ b/legacy/include/utils/SkDeferredCanvas.h
@@ -0,0 +1,302 @@
+/*
+ * 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 SkDeferredCanvas_DEFINED
+#define SkDeferredCanvas_DEFINED
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPicture.h"
+#include "SkPixelRef.h"
+
+/** \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.
+    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;
+
+    SkDeferredCanvas();
+
+    /** Construct a canvas with the specified device to draw into.
+        Equivalent to calling default constructor, then setDevice.
+        @param device Specifies a device for the canvas to draw into.
+    */
+    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.
+     *
+     *  @param device The device that the canvas will raw into
+     *  @return The device argument, for convenience.
+     */
+    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.
+     *  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.
+     */
+    DeviceContext* setDeviceContext(DeviceContext* deviceContext);
+
+    /**
+     *  Enable or disable deferred drawing. When deferral is disabled,
+     *  pending draw operations are immediately flushed and from then on,
+     *  the SkDeferredCanvas behaves just like a regular SkCanvas.
+     *  This method must not be called while the save/restore stack is in use.
+     *  @param deferred true/false
+     */
+    void setDeferredDrawing(bool deferred);
+
+    // Overrides of the SkCanvas interface
+    virtual int save(SaveFlags flags) SK_OVERRIDE;
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags) 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& 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,
+                            SkRegion::Op op) SK_OVERRIDE;
+    virtual void clear(SkColor) SK_OVERRIDE;
+    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 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,
+                                const SkRect& dst, const SkPaint* paint)
+                                SK_OVERRIDE;
+
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                const SkRect& dst, const SkPaint* paint)
+                                SK_OVERRIDE;
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, 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 drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPicture(SkPicture& picture) SK_OVERRIDE;
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) SK_OVERRIDE;
+    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 {
+    public:
+        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.
+         */
+        void setDeviceContext(DeviceContext* deviceContext);
+
+        /**
+         *  Returns the recording canvas.
+         */
+        SkCanvas* recordingCanvas() const {return fRecordingCanvas;}
+
+        /**
+         *  Returns the immediate (non deferred) canvas.
+         */
+        SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
+
+        /**
+         *  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;
+    };
+
+    DeferredDevice* getDeferredDevice() const;
+
+protected:
+    virtual SkCanvas* canvasForDrawIter();
+
+private:
+    SkCanvas* drawingCanvas() const;
+    bool isFullFrame(const SkRect*, const SkPaint*) const;
+    void validate() const;
+    void init();
+    bool            fDeferredDrawing;
+
+    typedef SkCanvas INHERITED;
+};
+
+
+#endif
diff --git a/legacy/include/utils/SkDumpCanvas.h b/legacy/include/utils/SkDumpCanvas.h
new file mode 100644
index 0000000..de2af04
--- /dev/null
+++ b/legacy/include/utils/SkDumpCanvas.h
@@ -0,0 +1,147 @@
+
+/*
+ * 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 SkDumpCanvas_DEFINED
+#define SkDumpCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+/** This class overrides all the draw methods on SkCanvas, and formats them
+    as text, and then sends that to a Dumper helper object.
+
+    Typical use might be to dump a display list to a log file to see what is
+    being drawn.
+ */
+class SkDumpCanvas : public SkCanvas {
+public:
+    class Dumper;
+
+    explicit SkDumpCanvas(Dumper* = 0);
+    virtual ~SkDumpCanvas();
+
+    enum Verb {
+        kNULL_Verb,
+
+        kSave_Verb,
+        kRestore_Verb,
+
+        kMatrix_Verb,
+
+        kClip_Verb,
+
+        kDrawPaint_Verb,
+        kDrawPoints_Verb,
+        kDrawRect_Verb,
+        kDrawPath_Verb,
+        kDrawBitmap_Verb,
+        kDrawText_Verb,
+        kDrawPicture_Verb,
+        kDrawVertices_Verb,
+        kDrawData_Verb
+    };
+
+    /** Subclasses of this are installed on the DumpCanvas, and then called for
+        each drawing command.
+     */
+    class Dumper : public SkRefCnt {
+    public:
+        virtual void dump(SkDumpCanvas*, SkDumpCanvas::Verb, const char str[],
+                          const SkPaint*) = 0;
+    };
+
+    Dumper* getDumper() const { return fDumper; }
+    void    setDumper(Dumper*);
+
+    int getNestLevel() const { return fNestLevel; }
+
+    virtual int save(SaveFlags) SK_OVERRIDE;
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags) 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&, 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;
+
+    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 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,
+                                const SkRect& dst, const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, 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 drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPicture(SkPicture&) SK_OVERRIDE;
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawData(const void*, size_t) SK_OVERRIDE;
+
+private:
+    Dumper* fDumper;
+    int     fNestLevel; // for nesting recursive elements like pictures
+
+    void dump(Verb, const SkPaint*, const char format[], ...);
+
+    typedef SkCanvas INHERITED;
+};
+
+/** Formats the draw commands, and send them to a function-pointer provided
+    by the caller.
+ */
+class SkFormatDumper : public SkDumpCanvas::Dumper {
+public:
+    SkFormatDumper(void (*)(const char text[], void* refcon), void* refcon);
+
+    // override from baseclass that does the formatting, and in turn calls
+    // the function pointer that was passed to the constructor
+    virtual void dump(SkDumpCanvas*, SkDumpCanvas::Verb, const char str[],
+                      const SkPaint*) SK_OVERRIDE;
+
+private:
+    void (*fProc)(const char*, void*);
+    void* fRefcon;
+
+    typedef SkDumpCanvas::Dumper INHERITED;
+};
+
+/** Subclass of Dumper that dumps the drawing command to SkDebugf
+ */
+class SkDebugfDumper : public SkFormatDumper {
+public:
+    SkDebugfDumper();
+
+private:
+    typedef SkFormatDumper INHERITED;
+};
+
+#endif
diff --git a/legacy/include/utils/SkInterpolator.h b/legacy/include/utils/SkInterpolator.h
new file mode 100644
index 0000000..289e886
--- /dev/null
+++ b/legacy/include/utils/SkInterpolator.h
@@ -0,0 +1,132 @@
+
+/*
+ * 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 SkInterpolator_DEFINED
+#define SkInterpolator_DEFINED
+
+#include "SkScalar.h"
+
+class SkInterpolatorBase : SkNoncopyable {
+public:
+    enum Result {
+        kNormal_Result,
+        kFreezeStart_Result,
+        kFreezeEnd_Result
+    };
+protected:
+    SkInterpolatorBase();
+    ~SkInterpolatorBase();
+public:
+    void    reset(int elemCount, int frameCount);
+
+    /** Return the start and end time for this interpolator.
+        If there are no key frames, return false.
+        @param startTime If not null, returns the time (in milliseconds) of the
+                         first keyframe. If there are no keyframes, this param
+                         is ignored (left unchanged).
+        @param endTime If not null, returns the time (in milliseconds) of the
+                       last keyframe. If there are no keyframes, this parameter
+                       is ignored (left unchanged).
+        @return True if there are key frames, or false if there are none.
+    */
+    bool    getDuration(SkMSec* startTime, SkMSec* endTime) const;
+
+
+    /** Set the whether the repeat is mirrored.
+        @param mirror If true, the odd repeats interpolate from the last key
+                      frame and the first.
+    */
+    void setMirror(bool mirror) {
+        fFlags = SkToU8((fFlags & ~kMirror) | (int)mirror);
+    }
+
+    /** Set the repeat count. The repeat count may be fractional.
+        @param repeatCount Multiplies the total time by this scalar.
+    */
+    void    setRepeatCount(SkScalar repeatCount) { fRepeat = repeatCount; }
+
+    /** Set the whether the repeat is mirrored.
+        @param reset If true, the odd repeats interpolate from the last key
+                     frame and the first.
+    */
+    void setReset(bool reset) {
+        fFlags = SkToU8((fFlags & ~kReset) | (int)reset);
+    }
+
+    Result  timeToT(SkMSec time, SkScalar* T, int* index, SkBool* exact) const;
+
+protected:
+    enum Flags {
+        kMirror = 1,
+        kReset = 2,
+        kHasBlend = 4
+    };
+    static SkScalar ComputeRelativeT(SkMSec time, SkMSec prevTime,
+                             SkMSec nextTime, const SkScalar blend[4] = NULL);
+    int16_t fFrameCount;
+    uint8_t fElemCount;
+    uint8_t fFlags;
+    SkScalar fRepeat;
+    struct SkTimeCode {
+        SkMSec  fTime;
+        SkScalar fBlend[4];
+    };
+    SkTimeCode* fTimes;     // pointer into fStorage
+    void* fStorage;
+#ifdef SK_DEBUG
+    SkTimeCode(* fTimesArray)[10];
+#endif
+};
+
+class SkInterpolator : public SkInterpolatorBase {
+public:
+    SkInterpolator();
+    SkInterpolator(int elemCount, int frameCount);
+    void    reset(int elemCount, int frameCount);
+
+    /** Add or replace a key frame, copying the values[] data into the
+        interpolator.
+        @param index    The index of this frame (frames must be ordered by time)
+        @param time The millisecond time for this frame
+        @param values   The array of values [elemCount] for this frame. The data
+                        is copied into the interpolator.
+        @param blend    A positive scalar specifying how to blend between this
+                        and the next key frame. [0...1) is a cubic lag/log/lag
+                        blend (slow to change at the beginning and end)
+                        1 is a linear blend (default)
+    */
+    bool setKeyFrame(int index, SkMSec time, const SkScalar values[],
+                     const SkScalar blend[4] = NULL);
+
+    /** Return the computed values given the specified time. Return whether
+        those values are the result of pinning to either the first
+        (kFreezeStart) or last (kFreezeEnd), or from interpolated the two
+        nearest key values (kNormal).
+        @param time The time to sample (in milliseconds)
+        @param (may be null) where to write the computed values.
+    */
+    Result timeToValues(SkMSec time, SkScalar values[] = NULL) const;
+
+    SkDEBUGCODE(static void UnitTest();)
+private:
+    SkScalar* fValues;  // pointer into fStorage
+#ifdef SK_DEBUG
+    SkScalar(* fScalarsArray)[10];
+#endif
+    typedef SkInterpolatorBase INHERITED;
+};
+
+/** Given all the parameters are [0...1], apply the cubic specified by (0,0)
+    (bx,by) (cx,cy) (1,1) to value, returning the answer, also [0...1].
+*/
+SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by,
+                           SkScalar cx, SkScalar cy);
+
+#endif
+
diff --git a/legacy/include/utils/SkJSON.h b/legacy/include/utils/SkJSON.h
new file mode 100644
index 0000000..5268af5
--- /dev/null
+++ b/legacy/include/utils/SkJSON.h
@@ -0,0 +1,285 @@
+/*
+ * 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 SkJSON_DEFINED
+#define SkJSON_DEFINED
+
+#include "SkTypes.h"
+
+class SkStream;
+class SkString;
+
+class SkJSON {
+public:
+    enum Type {
+        kObject,
+        kArray,
+        kString,
+        kInt,
+        kFloat,
+        kBool,
+    };
+    
+    class Array;
+    
+    class Object {
+    private:
+        struct Slot;
+
+    public:
+        Object();
+        Object(const Object&);
+        ~Object();
+
+        /**
+         *  Create a new slot with the specified name and value. The name
+         *  parameter is copied, but ownership of the Object parameter is
+         *  transferred. The Object parameter may be null, but the name must
+         *  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
+         *  transferred. The Array parameter may be null, but the name must
+         *  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.
+         */
+        void addBool(const char name[], bool value);
+
+        /**
+         *  Return the number of slots/fields in this object. These can be
+         *  iterated using Iter.
+         */
+        int count() const;
+
+        /**
+         *  Returns true if a slot matching the name and Type is found.
+         */
+        bool find(const char name[], Type) const;
+        bool findObject(const char name[], Object** = NULL) const;
+        bool findArray(const char name[], Array** = NULL) const;
+        bool findString(const char name[], SkString* = NULL) const;
+        bool findInt(const char name[], int32_t* = NULL) const;
+        bool findFloat(const char name[], float* = NULL) const;
+        bool findBool(const char name[], bool* = NULL) const;
+
+        /**
+         *  Finds the first slot matching the name and Type and removes it.
+         *  Returns true if found, false if not.
+         */
+        bool remove(const char name[], Type);
+
+        void toDebugf() const;
+
+        /**
+         *  Iterator class which returns all of the fields/slots in an Object,
+         *  in the order that they were added.
+         */
+        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.
+             */
+            bool done() const;
+
+            /**
+             *  Moves the iterator to the next element. Should only be called
+             *  if done() returns false.
+             */
+            void next();
+
+            /**
+             *  Returns the type of the current element. Should only be called
+             *  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.
+             */
+            bool boolValue() const;
+
+        private:
+            Slot* fSlot;
+        };
+
+    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:
+        /**
+         *  Creates an array with the specified Type and element count. All
+         *  entries are initialized to NULL/0/false.
+         */
+        Array(Type, int count);
+
+        /**
+         *  Creates an array of ints, initialized by copying the specified
+         *  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; }
+
+        /**
+         *  Replace the element at the specified index with the specified
+         *  Object (which may be null). Ownership of the Object is transferred.
+         *  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.
+         *  Should only be called if the Array's type is kArray.
+         */
+        void setArray(int index, Array*);
+
+        /**
+         *  Replace the element at the specified index with a copy of the
+         *  specified string (which may be null). Should only be called if the
+         *  Array's type is kString.
+         */
+        void setString(int index, const char str[]);
+
+        Object* const* objects() const {
+            SkASSERT(kObject == fType);
+            return fArray.fObjects;
+        }
+        Array* const* arrays() const {
+            SkASSERT(kObject == fType);
+            return fArray.fArrays;
+        }
+        const char* const* strings() const {
+            SkASSERT(kString == fType);
+            return fArray.fStrings;
+        }
+        int32_t* ints() const {
+            SkASSERT(kInt == fType);
+            return fArray.fInts;
+        }
+        float* floats() const {
+            SkASSERT(kFloat == fType);
+            return fArray.fFloats;
+        }
+        bool* bools() const {
+            SkASSERT(kBool == fType);
+            return fArray.fBools;
+        }
+
+    private:
+        int fCount;
+        Type fType;
+        union {
+            void*    fVoids;
+            Object** fObjects;
+            Array**  fArrays;
+            char**   fStrings;
+            int32_t* fInts;
+            float*   fFloats;
+            bool*    fBools;
+        } fArray;
+        
+        void init(Type, int count, const void* src);
+        void dumpLevel(int level) const;
+        
+        friend class Object;
+    };
+};
+
+#endif
diff --git a/legacy/include/utils/SkLayer.h b/legacy/include/utils/SkLayer.h
new file mode 100644
index 0000000..9cba463
--- /dev/null
+++ b/legacy/include/utils/SkLayer.h
@@ -0,0 +1,128 @@
+
+/*
+ * 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 SkLayer_DEFINED
+#define SkLayer_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+#include "SkColor.h"
+#include "SkMatrix.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkSize.h"
+
+class SkCanvas;
+
+class SkLayer : public SkRefCnt {
+
+public:
+    SkLayer();
+    SkLayer(const SkLayer&);
+    virtual ~SkLayer();
+
+    bool isInheritFromRootTransform() const;
+    SkScalar getOpacity() const { return m_opacity; }
+    const SkSize& getSize() const { return m_size; }
+    const SkPoint& getPosition() const { return m_position; }
+    const SkPoint& getAnchorPoint() const { return m_anchorPoint; }
+    const SkMatrix& getMatrix() const { return fMatrix; }
+    const SkMatrix& getChildrenMatrix() const { return fChildrenMatrix; }
+
+    SkScalar getWidth() const { return m_size.width(); }
+    SkScalar getHeight() const { return m_size.height(); }
+
+    void setInheritFromRootTransform(bool);
+    void setOpacity(SkScalar opacity) { m_opacity = opacity; }
+    void setSize(SkScalar w, SkScalar h) { m_size.set(w, h); }
+    void setPosition(SkScalar x, SkScalar y) { m_position.set(x, y); }
+    void setAnchorPoint(SkScalar x, SkScalar y) { m_anchorPoint.set(x, y); }
+    void setMatrix(const SkMatrix&);
+    void setChildrenMatrix(const SkMatrix&);
+
+    // children
+
+    /** Return the number of layers in our child list.
+     */
+    int countChildren() const;
+
+    /** Return the child at the specified index (starting at 0). This does not
+        affect the reference count of the child.
+     */
+    SkLayer* getChild(int index) const;
+
+    /** Add this layer to our child list at the end (top-most), and ref() it.
+        If it was already in another hierarchy, remove it from that list.
+        Return the new child.
+     */
+    SkLayer* addChild(SkLayer* child);
+
+    /** Remove this layer from its parent's list (or do nothing if it has no
+        parent.) If it had a parent, then unref() is called.
+     */
+    void detachFromParent();
+
+    /** Remove, and unref(), all of the layers in our child list.
+     */
+    void removeChildren();
+
+    /** Return our parent layer, or NULL if we have none.
+     */
+    SkLayer* getParent() const { return fParent; }
+
+    /** Return the root layer in this hiearchy. If this layer is the root
+        (i.e. has no parent), then this returns itself.
+     */
+    SkLayer* getRootLayer() const;
+
+    // coordinate system transformations
+
+    /** Return, in matrix, the matix transfomations that are applied locally
+        when this layer draws (i.e. its position and matrix/anchorPoint).
+        This does not include the childrenMatrix, since that is only applied
+        after this layer draws (but before its children draw).
+     */
+    void getLocalTransform(SkMatrix* matrix) const;
+
+    /** Return, in matrix, the concatenation of transforms that are applied
+        from this layer's root parent to the layer itself.
+        This is the matrix that is applied to the layer during drawing.
+     */
+    void localToGlobal(SkMatrix* matrix) const;
+
+    // paint method
+
+    void draw(SkCanvas*, SkScalar opacity);
+    void draw(SkCanvas* canvas) {
+        this->draw(canvas, SK_Scalar1);
+    }
+
+protected:
+    virtual void onDraw(SkCanvas*, SkScalar opacity);
+
+private:
+    enum Flags {
+        kInheritFromRootTransform_Flag = 0x01
+    };
+
+    SkLayer*    fParent;
+    SkScalar    m_opacity;
+    SkSize      m_size;
+    SkPoint     m_position;
+    SkPoint     m_anchorPoint;
+    SkMatrix    fMatrix;
+    SkMatrix    fChildrenMatrix;
+    uint32_t    fFlags;
+
+    SkTDArray<SkLayer*> m_children;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/legacy/include/utils/SkMatrix44.h b/legacy/include/utils/SkMatrix44.h
new file mode 100644
index 0000000..93140b0
--- /dev/null
+++ b/legacy/include/utils/SkMatrix44.h
@@ -0,0 +1,224 @@
+
+/*
+ * 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 SkMatrix44_DEFINED
+#define SkMatrix44_DEFINED
+
+#include "SkMatrix.h"
+#include "SkScalar.h"
+
+#ifdef SK_MSCALAR_IS_DOUBLE
+    typedef double SkMScalar;
+    static inline double SkFloatToMScalar(float x) {
+        return static_cast<double>(x);
+    }
+    static inline float SkMScalarToFloat(double x) {
+        return static_cast<float>(x);
+    }
+    static inline double SkDoubleToMScalar(double x) {
+        return x;
+    }
+    static inline double SkMScalarToDouble(double x) {
+        return x;
+    }
+    static const SkMScalar SK_MScalarPI = 3.141592653589793;
+#else
+    typedef float SkMScalar;
+    static inline float SkFloatToMScalar(float x) {
+        return x;
+    }
+    static inline float SkMScalarToFloat(float x) {
+        return x;
+    }
+    static inline float SkDoubleToMScalar(double x) {
+        return static_cast<float>(x);
+    }
+    static inline double SkMScalarToDouble(float x) {
+        return static_cast<double>(x);
+    }
+    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
+
+static const SkMScalar SK_MScalar1 = 1;
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkVector4 {
+    SkScalar fData[4];
+
+    SkVector4() {
+        this->set(0, 0, 0, 1);
+    }
+    SkVector4(const SkVector4& src) {
+        memcpy(fData, src.fData, sizeof(fData));
+    }
+    SkVector4(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
+        fData[0] = x;
+        fData[1] = y;
+        fData[2] = z;
+        fData[3] = w;
+    }
+
+    SkVector4& operator=(const SkVector4& src) {
+        memcpy(fData, src.fData, sizeof(fData));
+        return *this;
+    }
+
+    bool operator==(const SkVector4& v) {
+        return fData[0] == v.fData[0] && fData[1] == v.fData[1] &&
+               fData[2] == v.fData[2] && fData[3] == v.fData[3];
+    }
+    bool operator!=(const SkVector4& v) {
+        return !(*this == v);
+    }
+    bool equals(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
+        return fData[0] == x && fData[1] == y &&
+               fData[2] == z && fData[3] == w;
+    }
+
+    void set(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
+        fData[0] = x;
+        fData[1] = y;
+        fData[2] = z;
+        fData[3] = w;
+    }
+};
+
+class SK_API SkMatrix44 {
+public:
+    SkMatrix44();
+    SkMatrix44(const SkMatrix44&);
+    SkMatrix44(const SkMatrix44& a, const SkMatrix44& b);
+
+    SkMatrix44& operator=(const SkMatrix44& src) {
+        memcpy(this, &src, sizeof(*this));
+        return *this;
+    }
+
+    bool operator==(const SkMatrix44& other) const {
+        return !memcmp(this, &other, sizeof(*this));
+    }
+    bool operator!=(const SkMatrix44& other) const {
+        return !!memcmp(this, &other, sizeof(*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);
+
+    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();}
+
+    void set3x3(SkMScalar m00, SkMScalar m01, SkMScalar m02,
+                SkMScalar m10, SkMScalar m11, SkMScalar m12,
+                SkMScalar m20, SkMScalar m21, SkMScalar m22);
+
+    void setTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
+    void preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
+    void postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
+
+    void setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
+    void preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
+    void postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
+
+    void setScale(SkMScalar scale) {
+        this->setScale(scale, scale, scale);
+    }
+    void preScale(SkMScalar scale) {
+        this->preScale(scale, scale, scale);
+    }
+    void postScale(SkMScalar scale) {
+        this->postScale(scale, scale, scale);
+    }
+
+    void setRotateDegreesAbout(SkMScalar x, SkMScalar y, SkMScalar z,
+                               SkMScalar degrees) {
+        this->setRotateAbout(x, y, z, degrees * SK_MScalarPI / 180);
+    }
+
+    /** Rotate about the vector [x,y,z]. If that vector is not unit-length,
+        it will be automatically resized.
+     */
+    void setRotateAbout(SkMScalar x, SkMScalar y, SkMScalar z,
+                        SkMScalar radians);
+    /** Rotate about the vector [x,y,z]. Does not check the length of the
+        vector, assuming it is unit-length.
+     */
+    void setRotateAboutUnit(SkMScalar x, SkMScalar y, SkMScalar z,
+                            SkMScalar radians);
+
+    void setConcat(const SkMatrix44& a, const SkMatrix44& b);
+    void preConcat(const SkMatrix44& m) {
+        this->setConcat(*this, m);
+    }
+    void postConcat(const SkMatrix44& m) {
+        this->setConcat(m, *this);
+    }
+
+    friend SkMatrix44 operator*(const SkMatrix44& a, const SkMatrix44& b) {
+        return SkMatrix44(a, b);
+    }
+
+    /** If this is invertible, return that in inverse and return true. If it is
+        not invertible, return false and ignore the inverse parameter.
+     */
+    bool invert(SkMatrix44* inverse) const;
+
+    /** 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 map(SkScalar vec[4]) const {
+        this->map(vec, vec);
+    }
+
+    friend SkVector4 operator*(const SkMatrix44& m, const SkVector4& src) {
+        SkVector4 dst;
+        m.map(src.fData, dst.fData);
+        return dst;
+    }
+
+    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;
+};
+
+#endif
diff --git a/legacy/include/utils/SkMeshUtils.h b/legacy/include/utils/SkMeshUtils.h
new file mode 100644
index 0000000..c7cdeca
--- /dev/null
+++ b/legacy/include/utils/SkMeshUtils.h
@@ -0,0 +1,50 @@
+
+/*
+ * 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 SkMeshUtils_DEFINED
+#define SkMeshUtils_DEFINED
+
+#include "SkPoint.h"
+#include "SkColor.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkPaint;
+
+class SkMeshIndices {
+public:
+    SkMeshIndices();
+    ~SkMeshIndices();
+    
+    bool init(int texW, int texH, int rows, int cols) {
+        return this->init(NULL, NULL, texW, texH, rows, cols);
+    }
+
+    bool init(SkPoint tex[], uint16_t indices[],
+              int texW, int texH, int rows, int cols);
+
+    size_t          indexCount() const { return fIndexCount; }
+    const uint16_t* indices() const { return fIndices; }
+
+    size_t          texCount() const { return fTexCount; }
+    const SkPoint*  tex() const { return fTex; }
+
+private:
+    size_t      fIndexCount, fTexCount;
+    SkPoint*    fTex;
+    uint16_t*   fIndices;
+    void*       fStorage; // may be null
+};
+
+class SkMeshUtils {
+public:
+    static void Draw(SkCanvas*, const SkBitmap&, int rows, int cols,
+                     const SkPoint verts[], const SkColor colors[],
+                     const SkPaint& paint);
+};
+
+#endif
diff --git a/legacy/include/utils/SkNWayCanvas.h b/legacy/include/utils/SkNWayCanvas.h
new file mode 100644
index 0000000..dbf4a58
--- /dev/null
+++ b/legacy/include/utils/SkNWayCanvas.h
@@ -0,0 +1,84 @@
+
+/*
+ * 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 SkNWayCanvas_DEFINED
+#define SkNWayCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+class SkNWayCanvas : public SkCanvas {
+public:
+    SkNWayCanvas(int width, int height);
+    virtual ~SkNWayCanvas();
+
+    void addCanvas(SkCanvas*);
+    void removeCanvas(SkCanvas*);
+    void removeAll();
+
+    ///////////////////////////////////////////////////////////////////////////
+    // These are forwarded to the N canvases we're referencing
+
+    virtual int save(SaveFlags) SK_OVERRIDE;
+    virtual int saveLayer(const SkRect* bounds, const SkPaint*,
+                          SaveFlags) 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&, 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;
+
+    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 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,
+                                const SkRect& dst, const SkPaint*) SK_OVERRIDE;
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint*) SK_OVERRIDE;
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint*) SK_OVERRIDE;
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, const SkPaint&) SK_OVERRIDE;
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint&) SK_OVERRIDE;
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint&) SK_OVERRIDE;
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint&) SK_OVERRIDE;
+    virtual void drawPicture(SkPicture&) SK_OVERRIDE;
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint&) SK_OVERRIDE;
+
+    virtual SkBounder* setBounder(SkBounder*) SK_OVERRIDE;
+    virtual SkDrawFilter* setDrawFilter(SkDrawFilter*) SK_OVERRIDE;
+
+private:
+    SkTDArray<SkCanvas*> fList;
+
+    class Iter;
+
+    typedef SkCanvas INHERITED;
+};
+
+
+#endif
+
diff --git a/legacy/include/utils/SkNinePatch.h b/legacy/include/utils/SkNinePatch.h
new file mode 100644
index 0000000..b0ea46b
--- /dev/null
+++ b/legacy/include/utils/SkNinePatch.h
@@ -0,0 +1,33 @@
+
+/*
+ * 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 SkNinePatch_DEFINED
+#define SkNinePatch_DEFINED
+
+#include "SkRect.h"
+#include "SkRegion.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkPaint;
+
+class SkNinePatch {
+public:
+    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,
+                         const int32_t yDivs[], int numYDivs,
+                         const SkPaint* paint = NULL);
+};
+
+#endif
diff --git a/legacy/include/utils/SkParse.h b/legacy/include/utils/SkParse.h
new file mode 100644
index 0000000..7491cd6
--- /dev/null
+++ b/legacy/include/utils/SkParse.h
@@ -0,0 +1,37 @@
+
+/*
+ * 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 SkParse_DEFINED
+#define SkParse_DEFINED
+
+#include "SkColor.h"
+
+class SkParse {
+public:
+    static int Count(const char str[]); // number of scalars or int values
+    static int Count(const char str[], char separator);
+    static const char* FindColor(const char str[], SkColor* value);
+    static const char* FindHex(const char str[], uint32_t* value);
+    static const char* FindMSec(const char str[], SkMSec* value);
+    static const char* FindNamedColor(const char str[], size_t len, SkColor* color);
+    static const char* FindS32(const char str[], int32_t* value);
+    static const char* FindScalar(const char str[], SkScalar* value);
+    static const char* FindScalars(const char str[], SkScalar value[], int count);
+
+    static bool FindBool(const char str[], bool* value);
+    // return the index of str in list[], or -1 if not found
+    static int  FindList(const char str[], const char list[]);
+#ifdef SK_SUPPORT_UNITTEST
+    static void TestColor();
+    static void UnitTest();
+#endif
+};
+
+#endif
+
diff --git a/legacy/include/utils/SkParsePaint.h b/legacy/include/utils/SkParsePaint.h
new file mode 100644
index 0000000..b35fcf1
--- /dev/null
+++ b/legacy/include/utils/SkParsePaint.h
@@ -0,0 +1,27 @@
+
+/*
+ * 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 SkParsePaint_DEFINED
+#define SkParsePaint_DEFINED
+
+#include "SkPaint.h"
+#include "SkDOM.h"
+
+/** "color"             color
+    "opacity"           scalar  [0..1]
+    "stroke-width"      scalar  (0...inf)
+    "text-size"         scalar  (0..inf)
+    "is-stroke"         bool
+    "is-antialias"      bool
+    "is-lineartext"     bool
+*/
+void SkPaint_Inflate(SkPaint*, const SkDOM&, const SkDOM::Node*);
+
+#endif
+
diff --git a/legacy/include/utils/SkParsePath.h b/legacy/include/utils/SkParsePath.h
new file mode 100644
index 0000000..b48c2c5
--- /dev/null
+++ b/legacy/include/utils/SkParsePath.h
@@ -0,0 +1,24 @@
+
+/*
+ * 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 SkParsePath_DEFINED
+#define SkParsePath_DEFINED
+
+#include "SkPath.h"
+
+class SkString;
+
+class SkParsePath {
+public:
+    static bool FromSVGString(const char str[], SkPath*);
+    static void ToSVGString(const SkPath&, SkString*);
+};
+
+#endif
+
diff --git a/legacy/include/utils/SkProxyCanvas.h b/legacy/include/utils/SkProxyCanvas.h
new file mode 100644
index 0000000..720436b
--- /dev/null
+++ b/legacy/include/utils/SkProxyCanvas.h
@@ -0,0 +1,86 @@
+
+/*
+ * 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 SkProxyCanvas_DEFINED
+#define SkProxyCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+/** This class overrides all virtual methods on SkCanvas, and redirects them
+    to a "proxy", another SkCanvas instance. It can be the basis for
+    intercepting (and possibly modifying) calls to a canvas.
+
+    There must be a proxy installed before the proxycanvas can be used (i.e.
+    before its virtual methods can be called).
+ */
+class SkProxyCanvas : public SkCanvas {
+public:
+    SkProxyCanvas() : fProxy(NULL) {}
+    SkProxyCanvas(SkCanvas* proxy);
+    virtual ~SkProxyCanvas();
+
+    SkCanvas*   getProxy() const { return fProxy; }
+    void        setProxy(SkCanvas* proxy);
+
+    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&, 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;
+
+    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 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,
+                                const SkRect& dst, const SkPaint* paint = NULL) SK_OVERRIDE;
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint* paint = NULL) SK_OVERRIDE;
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint = NULL) SK_OVERRIDE;
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, 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 drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPicture(SkPicture&) SK_OVERRIDE;
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawData(const void* data, size_t length) SK_OVERRIDE;
+
+    virtual SkBounder* setBounder(SkBounder* bounder) SK_OVERRIDE;
+    virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter) SK_OVERRIDE;
+
+private:
+    SkCanvas*   fProxy;
+
+    typedef SkCanvas INHERITED;
+};
+
+#endif
diff --git a/include/utils/SkSfntUtils.h b/legacy/include/utils/SkSfntUtils.h
similarity index 100%
rename from include/utils/SkSfntUtils.h
rename to legacy/include/utils/SkSfntUtils.h
diff --git a/include/utils/SkTextBox.h b/legacy/include/utils/SkTextBox.h
similarity index 100%
rename from include/utils/SkTextBox.h
rename to legacy/include/utils/SkTextBox.h
diff --git a/legacy/include/utils/SkUnitMappers.h b/legacy/include/utils/SkUnitMappers.h
new file mode 100644
index 0000000..a14f1af
--- /dev/null
+++ b/legacy/include/utils/SkUnitMappers.h
@@ -0,0 +1,58 @@
+
+/*
+ * 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 SkUnitMappers_DEFINED
+#define SkUnitMappers_DEFINED
+
+#include "SkUnitMapper.h"
+
+/** This discretizes the range [0...1) into N discret values.
+*/
+class SkDiscreteMapper : public SkUnitMapper {
+public:
+    SkDiscreteMapper(int segments);
+    // override from SkUnitMapper
+    virtual uint16_t mapUnit16(uint16_t x);
+
+protected:
+    SkDiscreteMapper(SkFlattenableReadBuffer& );
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory();
+private:
+    int     fSegments;
+    SkFract fScale;    // computed from fSegments
+
+    static SkFlattenable* Create(SkFlattenableReadBuffer& buffer);
+    
+    typedef SkUnitMapper INHERITED;
+};
+
+/** This returns cos(x), to simulate lighting a sphere, where 0 maps to the
+    center of the sphere, and 1 maps to the edge.
+*/
+class SkCosineMapper : public SkUnitMapper {
+public:
+    SkCosineMapper() {}
+    // override from SkUnitMapper
+    virtual uint16_t mapUnit16(uint16_t x);
+
+protected:
+    SkCosineMapper(SkFlattenableReadBuffer&);
+    // overrides from SkFlattenable
+    virtual Factory getFactory();
+
+private:
+    static SkFlattenable* Create(SkFlattenableReadBuffer&);
+
+    typedef SkUnitMapper INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/utils/SkWGL.h b/legacy/include/utils/SkWGL.h
new file mode 100644
index 0000000..8eae5af
--- /dev/null
+++ b/legacy/include/utils/SkWGL.h
@@ -0,0 +1,90 @@
+
+/*
+ * 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 "SkRefCnt.h"
+
+#ifndef SkWGL_DEFINED
+#define SkWGL_DEFINED
+
+/**
+ * Working with WGL extensions can be a pain. Among the reasons is that You must
+ * have a GL context to get the proc addresses, but you want to use the procs to
+ * create a context in the first place. So you have to create a dummy GL ctx to
+ * get the proc addresses.
+ *
+ * This file helps by providing SkCreateWGLInterface(). It returns a struct of
+ * function pointers that it initializes. It also has a helper function to query
+ * for WGL extensions. It handles the fact that wglGetExtensionsString is itself
+ * an extension.
+ */
+
+#if !defined(WIN32_LEAN_AND_MEAN)
+    #define WIN32_LEAN_AND_MEAN
+    #define SK_LOCAL_LEAN_AND_MEAN
+#endif
+#include <Windows.h>
+#if defined(SK_LOCAL_LEAN_AND_MEAN)
+    #undef WIN32_LEAN_AND_MEAN
+    #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
+
+class SkWGLExtensions {
+public:
+    SkWGLExtensions();
+    /**
+     * Determines if an extensions is available for a given DC.
+     * WGL_extensions_string is considered a prerequisite for all other
+     * extensions. It is necessary to check this before calling other class
+     * functions.
+     */
+    bool hasExtension(HDC dc, const char* ext) const;
+
+    const char* getExtensionsString(HDC hdc) const;
+    BOOL choosePixelFormat(HDC hdc, const int*, const FLOAT*, UINT, int*, UINT*) const;
+    BOOL getPixelFormatAttribiv(HDC, int, int, UINT, const int*, int*) const;
+    BOOL getPixelFormatAttribfv(HDC hdc, int, int, UINT, const int*, FLOAT*) const;
+    HGLRC createContextAttribs(HDC, HGLRC, const int *) const;
+
+private:
+    typedef const char* (WINAPI *GetExtensionsStringProc)(HDC hdc);
+    typedef BOOL (WINAPI *ChoosePixelFormatProc)(HDC hdc, const int *, const FLOAT *, UINT, int *, UINT *);
+    typedef BOOL (WINAPI *GetPixelFormatAttribivProc)(HDC, int, int, UINT, const int*, int*);
+    typedef BOOL (WINAPI *GetPixelFormatAttribfvProc)(HDC hdc, int, int, UINT, const int*, FLOAT*);
+    typedef HGLRC (WINAPI *CreateContextAttribsProc)(HDC hDC, HGLRC, const int *);
+
+    GetExtensionsStringProc fGetExtensionsString;
+    ChoosePixelFormatProc fChoosePixelFormat;
+    GetPixelFormatAttribfvProc fGetPixelFormatAttribfv;
+    GetPixelFormatAttribivProc fGetPixelFormatAttribiv;
+    CreateContextAttribsProc fCreateContextAttribs;
+};
+
+#endif
diff --git a/include/utils/android/AndroidKeyToSkKey.h b/legacy/include/utils/android/AndroidKeyToSkKey.h
similarity index 100%
copy from include/utils/android/AndroidKeyToSkKey.h
copy to legacy/include/utils/android/AndroidKeyToSkKey.h
diff --git a/legacy/include/utils/ios/SkStream_NSData.h b/legacy/include/utils/ios/SkStream_NSData.h
new file mode 100755
index 0000000..0829a4f
--- /dev/null
+++ b/legacy/include/utils/ios/SkStream_NSData.h
@@ -0,0 +1,41 @@
+
+/*
+ * 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 SkStream_NSData_DEFINED
+#define SkStream_NSData_DEFINED
+
+#import <UIKit/UIKit.h>
+#include "SkStream.h"
+
+/** Returns an NSData with a copy of the stream's data. The caller must call
+    retain if it intends to keep the data object beyond the current stack-frame
+    (i.e. internally we're calling [NSData dataWithBytes...]
+ */
+NSData* NSData_dataWithStream(SkStream* stream);
+
+/** Returns an NSData from the named resource (from main bundle).
+    The caller must call retain if it intends to keep the data object beyond
+    the current stack-frame
+    (i.e. internally we're calling [NSData dataWithContentsOfMappedFile...]
+ */
+NSData* NSData_dataFromResource(const char name[], const char suffix[]);
+
+/** Wrap a stream around NSData.
+ */
+class SkStream_NSData : public SkMemoryStream {
+public:
+            SkStream_NSData(NSData* data);
+    virtual ~SkStream_NSData();
+    
+    static SkStream_NSData* CreateFromResource(const char name[],
+                                               const char suffix[]);
+
+private:
+    NSData* fNSData;
+};
+
+#endif
diff --git a/legacy/include/utils/mac/SkCGUtils.h b/legacy/include/utils/mac/SkCGUtils.h
new file mode 100644
index 0000000..46f8996
--- /dev/null
+++ b/legacy/include/utils/mac/SkCGUtils.h
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkCGUtils_DEFINED
+#define SkCGUtils_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+class SkBitmap;
+class SkStream;
+
+/**
+ *  Create an imageref from the specified bitmap using the specified colorspace.
+ *  If space is NULL, then CGColorSpaceCreateDeviceRGB() is used.
+ */
+SK_API CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
+                                                   CGColorSpaceRef space);
+
+/**
+ *  Create an imageref from the specified bitmap using the colorspace returned
+ *  by CGColorSpaceCreateDeviceRGB()
+ */
+static inline CGImageRef SkCreateCGImageRef(const SkBitmap& bm) {
+    return SkCreateCGImageRefWithColorspace(bm, NULL);
+}
+
+/**
+ *  Draw the bitmap into the specified CG context. The bitmap will be converted
+ *  to a CGImage using the generic RGB colorspace. (x,y) specifies the position
+ *  of the top-left corner of the bitmap. The bitmap is converted using the
+ *  colorspace returned by CGColorSpaceCreateDeviceRGB()
+ */
+void SkCGDrawBitmap(CGContextRef, const SkBitmap&, float x, float y);
+
+bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output);
+
+/**
+ *  Return a provider that wraps the specified stream. It will become an
+ *  owner of the stream, so the caller must still manage its ownership.
+ *
+ *  To hand-off ownership of the stream to the provider, the caller must do
+ *  something like the following:
+ *
+ *  SkStream* stream = new ...;
+ *  CGDataProviderRef provider = SkStreamToDataProvider(stream);
+ *  stream->unref();
+ *
+ *  Now when the provider is finally deleted, it will delete the stream.
+ */
+CGDataProviderRef SkCreateDataProviderFromStream(SkStream*);
+
+#endif
diff --git a/include/utils/unix/XkeysToSkKeys.h b/legacy/include/utils/unix/XkeysToSkKeys.h
similarity index 100%
copy from include/utils/unix/XkeysToSkKeys.h
copy to legacy/include/utils/unix/XkeysToSkKeys.h
diff --git a/include/utils/unix/keysym2ucs.h b/legacy/include/utils/unix/keysym2ucs.h
similarity index 100%
copy from include/utils/unix/keysym2ucs.h
copy to legacy/include/utils/unix/keysym2ucs.h
diff --git a/legacy/include/utils/win/SkAutoCoInitialize.h b/legacy/include/utils/win/SkAutoCoInitialize.h
new file mode 100644
index 0000000..709fa6b
--- /dev/null
+++ b/legacy/include/utils/win/SkAutoCoInitialize.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkAutoCo_DEFINED
+#define SkAutoCo_DEFINED
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include "SkTemplates.h"
+
+/**
+ * An instance of this class initializes COM on creation
+ * and closes the COM library on destruction.
+ */
+class SkAutoCoInitialize : SkNoncopyable {
+private:
+    HRESULT fHR;
+public:
+    SkAutoCoInitialize();
+    ~SkAutoCoInitialize();
+    bool succeeded();
+};
+
+#endif
diff --git a/legacy/include/utils/win/SkHRESULT.h b/legacy/include/utils/win/SkHRESULT.h
new file mode 100644
index 0000000..ff596c7
--- /dev/null
+++ b/legacy/include/utils/win/SkHRESULT.h
@@ -0,0 +1,50 @@
+/*
+ * 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 SkHRESULT_DEFINED
+#define SkHRESULT_DEFINED
+
+#include "SkTypes.h"
+
+void SkTraceHR(const char* file, unsigned long line,
+               HRESULT hr, const char* msg);
+
+#ifdef SK_DEBUG
+#define SK_TRACEHR(_hr, _msg) SkTraceHR(__FILE__, __LINE__, _hr, _msg)
+#else
+#define SK_TRACEHR(_hr, _msg) _hr
+#endif
+
+#define HR_GENERAL(_ex, _msg, _ret) {\
+    HRESULT _hr = _ex;\
+    if (FAILED(_hr)) {\
+        SK_TRACEHR(_hr, _msg);\
+        return _ret;\
+    }\
+}
+
+//@{
+/**
+These macros are for reporting HRESULT errors.
+The expression will be evaluated.
+If the resulting HRESULT SUCCEEDED then execution will continue normally.
+If the HRESULT FAILED then the macro will return from the current function.
+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 HRV variants will simply return when FAILED.
+*/
+#define HR(ex) HR_GENERAL(ex, NULL, _hr)
+#define HRM(ex, msg) HR_GENERAL(ex, msg, _hr)
+
+#define HRB(ex) HR_GENERAL(ex, NULL, false)
+#define HRBM(ex, msg) HR_GENERAL(ex, msg, false)
+
+#define HRV(ex) HR_GENERAL(ex, NULL, )
+#define HRVM(ex, msg) HR_GENERAL(ex, msg, )
+//@}
+#endif
diff --git a/legacy/include/utils/win/SkIStream.h b/legacy/include/utils/win/SkIStream.h
new file mode 100644
index 0000000..b7d0949
--- /dev/null
+++ b/legacy/include/utils/win/SkIStream.h
@@ -0,0 +1,131 @@
+
+/*
+ * 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 SkIStream_DEFINED
+#define SkIStream_DEFINED
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <ole2.h>
+
+class SkStream;
+class SkWStream;
+
+/**
+ * A bare IStream implementation which properly reference counts
+ * but returns E_NOTIMPL for all ISequentialStream and IStream methods.
+ */
+class SkBaseIStream : public IStream {
+private:
+    LONG _refcount;
+
+protected:
+    explicit SkBaseIStream();
+    virtual ~SkBaseIStream();
+
+public:
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid
+                                                   , void ** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef(void);
+    virtual ULONG STDMETHODCALLTYPE Release(void);
+
+    // ISequentialStream Interface
+public:
+    virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead);
+
+    virtual HRESULT STDMETHODCALLTYPE Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten);
+
+    // 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
+                                         , DWORD dwOrigin
+                                         , ULARGE_INTEGER* lpNewFilePointer);
+
+    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag);
+};
+
+/**
+ * A minimal read-only IStream implementation which wraps an SkIStream.
+ */
+class SkIStream : public SkBaseIStream {
+private:
+    SkStream *fSkStream;
+    bool fUnrefOnRelease;
+    ULARGE_INTEGER fLocation;
+
+    SkIStream(SkStream* stream, bool unrefOnRelease);
+    virtual ~SkIStream();
+
+public:
+    HRESULT static CreateFromSkStream(SkStream* stream
+                                    , bool unrefOnRelease
+                                    , IStream ** ppStream);
+
+    virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead);
+
+    virtual HRESULT STDMETHODCALLTYPE Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten);
+
+    virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove
+                                         , DWORD dwOrigin
+                                         , ULARGE_INTEGER* lpNewFilePointer);
+
+    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag);
+};
+
+/**
+ * A minimal write-only IStream implementation which wraps an SkWIStream.
+ */
+class SkWIStream : public SkBaseIStream {
+private:
+    SkWStream *fSkWStream;
+
+    SkWIStream(SkWStream* stream);
+    virtual ~SkWIStream();
+
+public:
+    HRESULT static CreateFromSkWStream(SkWStream* stream, IStream ** ppStream);
+
+    virtual HRESULT STDMETHODCALLTYPE Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten);
+
+    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD);
+
+    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag);
+};
+
+#endif
diff --git a/legacy/include/utils/win/SkTScopedComPtr.h b/legacy/include/utils/win/SkTScopedComPtr.h
new file mode 100644
index 0000000..b9be037
--- /dev/null
+++ b/legacy/include/utils/win/SkTScopedComPtr.h
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSkTScopedPtr_DEFINED
+#define SkSkTScopedPtr_DEFINED
+
+#include "SkTypes.h"
+#include "SkTemplates.h"
+
+template<typename T>
+class SkBlockComRef : public T {
+private:
+    virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
+    virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
+};
+
+template<typename T>
+class SkTScopedComPtr : SkNoncopyable {
+private:
+    T *fPtr;
+
+public:
+    explicit SkTScopedComPtr(T *ptr = NULL) : fPtr(ptr) { }
+    ~SkTScopedComPtr() {
+        this->reset();
+    }
+    T &operator*() const { return *fPtr; }
+    SkBlockComRef<T> *operator->() const {
+        return static_cast<SkBlockComRef<T>*>(fPtr);
+    }
+    /**
+     * Returns the address of the underlying pointer.
+     * This is dangerous -- it breaks encapsulation and the reference escapes.
+     * Must only be used on instances currently pointing to NULL,
+     * and only to initialize the instance.
+     */
+    T **operator&() { SkASSERT(fPtr == NULL); return &fPtr; }
+    T *get() const { return fPtr; }
+    void reset() {
+        if (NULL != this->fPtr) {
+            this->fPtr->Release();
+            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;
+        return temp;
+    }
+};
+
+#endif
diff --git a/legacy/include/views/SkApplication.h b/legacy/include/views/SkApplication.h
new file mode 100644
index 0000000..8369f68
--- /dev/null
+++ b/legacy/include/views/SkApplication.h
@@ -0,0 +1,19 @@
+
+/*
+ * 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 SkApplication_DEFINED
+#define SkApplication_DEFINED
+
+class SkOSWindow;
+
+extern SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv);
+extern void application_init();
+extern void application_term();
+
+#endif // SkApplication_DEFINED
diff --git a/legacy/include/views/SkBGViewArtist.h b/legacy/include/views/SkBGViewArtist.h
new file mode 100644
index 0000000..869beab
--- /dev/null
+++ b/legacy/include/views/SkBGViewArtist.h
@@ -0,0 +1,34 @@
+
+/*
+ * 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 SkBGViewArtist_DEFINED
+#define SkBGViewArtist_DEFINED
+
+#include "SkView.h"
+#include "SkPaint.h"
+
+class SkBGViewArtist : public SkView::Artist {
+public:
+            SkBGViewArtist(SkColor c = SK_ColorWHITE);
+    virtual ~SkBGViewArtist();
+
+    const SkPaint&  paint() const { return fPaint; }
+    SkPaint&        paint() { return fPaint; }
+
+protected:
+    // overrides
+    virtual void onDraw(SkView*, SkCanvas*);
+    virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+    SkPaint fPaint;
+};
+
+#endif
+
diff --git a/include/views/SkBorderView.h b/legacy/include/views/SkBorderView.h
similarity index 100%
rename from include/views/SkBorderView.h
rename to legacy/include/views/SkBorderView.h
diff --git a/legacy/include/views/SkEvent.h b/legacy/include/views/SkEvent.h
new file mode 100644
index 0000000..b3a07e9
--- /dev/null
+++ b/legacy/include/views/SkEvent.h
@@ -0,0 +1,295 @@
+
+/*
+ * 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 SkEvent_DEFINED
+#define SkEvent_DEFINED
+
+#include "SkDOM.h"
+#include "SkMetaData.h"
+#include "SkString.h"
+
+/** Unique 32bit id used to identify an instance of SkEventSink. When events are
+    posted, they are posted to a specific sinkID. When it is time to dispatch the
+    event, the sinkID is used to find the specific SkEventSink object. If it is found,
+    its doEvent() method is called with the event.
+*/
+typedef uint32_t SkEventSinkID;
+
+/**
+ *  \class SkEvent
+ *
+ *  When an event is dispatched from the event queue, it is either sent to
+ *  the eventsink matching the target ID (if not 0), or the target proc is
+ *  called (if not NULL).
+ */
+class SkEvent {
+public:
+    /**
+     *  Function pointer that takes an event, returns true if it "handled" it.
+     */
+    typedef bool (*Proc)(const SkEvent& evt);
+
+    SkEvent();
+    explicit SkEvent(const SkString& type, SkEventSinkID = 0);
+    explicit SkEvent(const char type[], SkEventSinkID = 0);
+    SkEvent(const SkEvent& src);
+    ~SkEvent();
+
+    /** Copy the event's type into the specified SkString parameter */
+    void getType(SkString* str) const;
+
+    /** Returns true if the event's type matches exactly the specified type (case sensitive) */
+    bool isType(const SkString& str) const;
+
+    /** Returns true if the event's type matches exactly the specified type (case sensitive) */
+    bool isType(const char type[], size_t len = 0) const;
+
+    /**
+     *  Set the event's type to the specified string.
+     */
+    void setType(const SkString&);
+
+    /**
+     *  Set the event's type to the specified string.
+     */
+    void setType(const char type[], size_t len = 0);
+
+    /**
+     *  Return the target ID, or 0 if there is none.
+     *
+     *  When an event is dispatched from the event queue, it is either sent to
+     *  the eventsink matching the targetID (if not 0), or the target proc is
+     *  called (if not NULL).
+     */
+    SkEventSinkID getTargetID() const { return fTargetID; }
+
+    /**
+     *  Set the target ID for this event. 0 means none. Calling this will
+     *  automatically clear the targetProc to null.
+     *
+     *  When an event is dispatched from the event queue, it is either sent to
+     *  the eventsink matching the targetID (if not 0), or the target proc is
+     *  called (if not NULL).
+     */
+    SkEvent* setTargetID(SkEventSinkID targetID) {
+        fTargetProc = NULL;
+        fTargetID = targetID;
+        return this;
+    }
+
+    /**
+     *  Return the target proc, or NULL if it has none.
+     *
+     *  When an event is dispatched from the event queue, it is either sent to
+     *  the eventsink matching the targetID (if not 0), or the target proc is
+     *  called (if not NULL).
+     */
+    Proc getTargetProc() const { return fTargetProc; }
+
+    /**
+     *  Set the target ID for this event. NULL means none. Calling this will
+     *  automatically clear the targetID to 0.
+     *
+     *  When an event is dispatched from the event queue, it is either sent to
+     *  the eventsink matching the targetID (if not 0), or the target proc is
+     *  called (if not NULL).
+     */
+    SkEvent* setTargetProc(Proc proc) {
+        fTargetID = 0;
+        fTargetProc = proc;
+        return this;
+    }
+    
+    /**
+     *  Return the event's unnamed 32bit field. Default value is 0
+     */
+    uint32_t getFast32() const { return f32; }
+
+    /**
+     *  Set the event's unnamed 32bit field.
+     */
+    void setFast32(uint32_t x) { f32 = x; }
+
+    /** Return true if the event contains the named 32bit field, and return the field
+        in value (if value is non-null). If there is no matching named field, return false
+        and ignore the value parameter.
+    */
+    bool findS32(const char name[], int32_t* value = NULL) const { return fMeta.findS32(name, value); }
+    /** Return true if the event contains the named SkScalar field, and return the field
+        in value (if value is non-null). If there is no matching named field, return false
+        and ignore the value parameter.
+    */
+    bool findScalar(const char name[], SkScalar* value = NULL) const { return fMeta.findScalar(name, value); }
+    /** Return true if the event contains the named SkScalar field, and return the fields
+        in value[] (if value is non-null), and return the number of SkScalars in count (if count is non-null).
+        If there is no matching named field, return false and ignore the value and count parameters.
+    */
+    const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = NULL) const { return fMeta.findScalars(name, count, values); }
+    /** Return the value of the named string field, or if no matching named field exists, return null.
+    */
+    const char* findString(const char name[]) const { return fMeta.findString(name); }
+    /** Return true if the event contains the named pointer field, and return the field
+        in value (if value is non-null). If there is no matching named field, return false
+        and ignore the value parameter.
+    */
+    bool findPtr(const char name[], void** value) const { return fMeta.findPtr(name, value); }
+    bool findBool(const char name[], bool* value) const { return fMeta.findBool(name, value); }
+    const void* findData(const char name[], size_t* byteCount = NULL) const {
+        return fMeta.findData(name, byteCount);
+    }
+
+    /** Returns true if ethe event contains the named 32bit field, and if it equals the specified value */
+    bool hasS32(const char name[], int32_t value) const { return fMeta.hasS32(name, value); }
+    /** Returns true if ethe event contains the named SkScalar field, and if it equals the specified value */
+    bool hasScalar(const char name[], SkScalar value) const { return fMeta.hasScalar(name, value); }
+    /** Returns true if ethe event contains the named string field, and if it equals (using strcmp) the specified value */
+    bool hasString(const char name[], const char value[]) const { return fMeta.hasString(name, value); }
+    /** Returns true if ethe event contains the named pointer field, and if it equals the specified value */
+    bool hasPtr(const char name[], void* value) const { return fMeta.hasPtr(name, value); }
+    bool hasBool(const char name[], bool value) const { return fMeta.hasBool(name, value); }
+    bool hasData(const char name[], const void* data, size_t byteCount) const {
+        return fMeta.hasData(name, data, byteCount);
+    }
+
+    /** Add/replace the named 32bit field to the event. In XML use the subelement <data name=... s32=... /> */
+    void setS32(const char name[], int32_t value) { fMeta.setS32(name, value); }
+    /** Add/replace the named SkScalar field to the event. In XML use the subelement <data name=... scalar=... /> */
+    void setScalar(const char name[], SkScalar value) { fMeta.setScalar(name, value); }
+    /** Add/replace the named SkScalar[] field to the event. */
+    SkScalar* setScalars(const char name[], int count, const SkScalar values[] = NULL) { return fMeta.setScalars(name, count, values); }
+    /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */
+    void setString(const char name[], const SkString& value) { fMeta.setString(name, value.c_str()); }
+    /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */
+    void setString(const char name[], const char value[]) { fMeta.setString(name, value); }
+    /** Add/replace the named pointer field to the event. There is no XML equivalent for this call */
+    void setPtr(const char name[], void* value) { fMeta.setPtr(name, value); }
+    void setBool(const char name[], bool value) { fMeta.setBool(name, value); }
+    void setData(const char name[], const void* data, size_t byteCount) {
+        fMeta.setData(name, data, byteCount);
+    }
+
+    /** Return the underlying metadata object */
+    SkMetaData& getMetaData() { return fMeta; }
+    /** Return the underlying metadata object */
+    const SkMetaData& getMetaData() const { return fMeta; }
+
+    /** Call this to initialize the event from the specified XML node */
+    void inflate(const SkDOM&, const SkDOM::Node*);
+
+    SkDEBUGCODE(void dump(const char title[] = NULL);)
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     *  Post to the event queue using the event's targetID or target-proc.
+     *
+     *  The event must be dynamically allocated, as ownership is transferred to
+     *  the event queue. It cannot be allocated on the stack or in a global.
+     */
+    void post() {
+        return this->postDelay(0);
+    }
+    
+    /**
+     *  Post to the event queue using the event's targetID or target-proc and
+     *  the specifed millisecond delay.
+     *
+     *  The event must be dynamically allocated, as ownership is transferred to
+     *  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
+     *  time, as measured by SkTime::GetMSecs().
+     *
+     *  The event must be dynamically allocated, as ownership is transferred to
+     *  the event queue. It cannot be allocated on the stack or in a global.
+     */
+    void postTime(SkMSec time);
+
+    ///////////////////////////////////////////////
+    /** Porting layer must call these functions **/
+    ///////////////////////////////////////////////
+
+    /** Global initialization function for the SkEvent system. Should be called exactly
+        once before any other event method is called, and should be called after the
+        call to SkGraphics::Init().
+    */
+    static void Init();
+    /** Global cleanup function for the SkEvent system. Should be called exactly once after
+        all event methods have been called, and should be called before calling SkGraphics::Term().
+    */
+    static void Term();
+
+    /** Call this to process one event from the queue. If it returns true, there are more events
+        to process.
+    */
+    static bool ProcessEvent();
+    /** Call this whenever the requested timer has expired (requested by a call to SetQueueTimer).
+        It will post any delayed events whose time as "expired" onto the event queue.
+        It may also call SignalQueueTimer() and SignalNonEmptyQueue().
+    */
+    static void ServiceQueueTimer();
+
+    /** Return the number of queued events. note that this value may be obsolete
+        upon return, since another thread may have called ProcessEvent() or
+        Post() after the count was made.
+     */
+    static int CountEventsOnQueue();
+
+    ////////////////////////////////////////////////////
+    /** Porting layer must implement these functions **/
+    ////////////////////////////////////////////////////
+
+    /** Called whenever an SkEvent is posted to an empty queue, so that the OS
+        can be told to later call Dequeue().
+    */
+    static void SignalNonEmptyQueue();
+    /** Called whenever the delay until the next delayed event changes. If zero is
+        passed, then there are no more queued delay events.
+    */
+    static void SignalQueueTimer(SkMSec delay);
+
+#ifndef SK_USE_WXWIDGETS
+#ifdef 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:
+    SkMetaData      fMeta;
+    mutable char*   fType;  // may be characters with low bit set to know that it is not a pointer
+    uint32_t        f32;
+
+    // 'there can be only one' (non-zero) between target-id and target-proc
+    SkEventSinkID   fTargetID;
+    Proc            fTargetProc;
+
+    // these are for our implementation of the event queue
+    SkMSec          fTime;
+    SkEvent*        fNextEvent; // either in the delay or normal event queue
+
+    void initialize(const char* type, size_t typeLen, SkEventSinkID);
+
+    static bool Enqueue(SkEvent* evt);
+    static SkMSec EnqueueTime(SkEvent* evt, SkMSec time);
+    static SkEvent* Dequeue();
+    static bool     QHasEvents();
+};
+
+#endif
+
diff --git a/legacy/include/views/SkEventSink.h b/legacy/include/views/SkEventSink.h
new file mode 100644
index 0000000..69981fa
--- /dev/null
+++ b/legacy/include/views/SkEventSink.h
@@ -0,0 +1,109 @@
+
+/*
+ * 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 SkEventSink_DEFINED
+#define SkEventSink_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkEvent.h"
+
+struct SkTagList;
+
+/** \class SkEventSink
+
+    SkEventSink is the base class for all objects that receive SkEvents.
+*/
+class SkEventSink : public SkRefCnt {
+public:
+            SkEventSink();
+    virtual ~SkEventSink();
+
+    /**
+     *  Returns this eventsink's unique ID. Use this to post SkEvents to
+     *  this eventsink.
+     */
+    SkEventSinkID getSinkID() const { return fID; }
+
+    /**
+     *  Call this to pass an event to this object for processing. Returns true if the
+     *  event was handled.
+     */
+    bool doEvent(const SkEvent&);
+
+    /** Returns true if the sink (or one of its subclasses) understands the event as a query.
+        If so, the sink may modify the event to communicate its "answer".
+    */
+    bool doQuery(SkEvent* query);
+
+    /**
+     *  Add sinkID to the list of listeners, to receive events from calls to sendToListeners()
+     *  and postToListeners(). If sinkID already exists in the listener list, no change is made.
+     */
+    void addListenerID(SkEventSinkID sinkID);
+
+    /**
+     *  Copy listeners from one event sink to another, typically from parent to child.
+     *  @param from the event sink to copy the listeners from
+     */
+    void copyListeners(const SkEventSink& from);
+
+    /**
+     *  Remove sinkID from the list of listeners. If sinkID does not appear in the list,
+     *  no change is made.
+     */
+    void removeListenerID(SkEventSinkID);
+
+    /**
+     *  Returns true if there are 1 or more listeners attached to this eventsink
+     */
+    bool hasListeners() const;
+
+    /**
+     *  Posts a copy of evt to each of the eventsinks in the lisener list.
+     *  This ignores the targetID and target proc in evt.
+     */
+    void postToListeners(const SkEvent& evt, SkMSec delay = 0);
+
+    enum EventResult {
+        kHandled_EventResult,       //!< the eventsink returned true from its doEvent method
+        kNotHandled_EventResult,    //!< the eventsink returned false from its doEvent method
+        kSinkNotFound_EventResult   //!< no matching eventsink was found for the event's getSink().
+    };
+
+    /**
+     *  DoEvent handles dispatching the event to its target ID or proc.
+     */
+    static EventResult DoEvent(const SkEvent&);
+
+    /**
+     *  Returns the matching eventsink, or null if not found
+     */
+    static SkEventSink* FindSink(SkEventSinkID);
+
+protected:
+    /** Override this to handle events in your subclass. Be sure to call the inherited version
+        for events that you don't handle.
+    */
+    virtual bool onEvent(const SkEvent&);
+    virtual bool onQuery(SkEvent*);
+
+    SkTagList*  findTagList(U8CPU tag) const;
+    void        addTagList(SkTagList*);
+    void        removeTagList(U8CPU tag);
+
+private:
+    SkEventSinkID   fID;
+    SkTagList*      fTagHead;
+
+    // for our private link-list
+    SkEventSink*    fNextSink;
+};
+
+#endif
+
diff --git a/include/views/SkImageView.h b/legacy/include/views/SkImageView.h
similarity index 100%
rename from include/views/SkImageView.h
rename to legacy/include/views/SkImageView.h
diff --git a/legacy/include/views/SkKey.h b/legacy/include/views/SkKey.h
new file mode 100644
index 0000000..4db3108
--- /dev/null
+++ b/legacy/include/views/SkKey.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 SkKey_DEFINED
+#define SkKey_DEFINED
+
+#include "SkTypes.h"
+
+enum SkKey {
+    //reordering these to match android.app.KeyEvent 
+    kNONE_SkKey,    //corresponds to android's UNKNOWN
+    
+    kLeftSoftKey_SkKey,
+    kRightSoftKey_SkKey,
+
+    kHome_SkKey,    //!< the home key - added to match android
+    kBack_SkKey,    //!< (CLR)
+    kSend_SkKey,    //!< the green (talk) key
+    kEnd_SkKey,     //!< the red key
+    
+    k0_SkKey,
+    k1_SkKey,
+    k2_SkKey,
+    k3_SkKey,
+    k4_SkKey,
+    k5_SkKey,
+    k6_SkKey,
+    k7_SkKey,
+    k8_SkKey,
+    k9_SkKey,
+    kStar_SkKey,    //!< the * key
+    kHash_SkKey,    //!< the # key
+
+    kUp_SkKey,
+    kDown_SkKey,
+    kLeft_SkKey,
+    kRight_SkKey,
+
+    kOK_SkKey,      //!< the center key
+
+    kVolUp_SkKey,   //!< volume up - match android
+    kVolDown_SkKey, //!< volume down - same
+    kPower_SkKey,   //!< power button - same
+    kCamera_SkKey,  //!< camera         - same
+
+    kSkKeyCount
+};
+
+#endif
+
diff --git a/legacy/include/views/SkOSMenu.h b/legacy/include/views/SkOSMenu.h
new file mode 100644
index 0000000..0c4a619
--- /dev/null
+++ b/legacy/include/views/SkOSMenu.h
@@ -0,0 +1,183 @@
+
+/*
+ * 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 SkOSMenu_DEFINED
+#define SkOSMenu_DEFINED
+
+#include "SkEvent.h"
+#include "SkTDArray.h"
+
+class SkOSMenu {
+public:
+    explicit SkOSMenu(const char title[] = "");
+    ~SkOSMenu();
+    
+    /**
+     * 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
+     *     List : int (selected index)
+     *     Segmented : int (selected index)
+     *     Slider : float
+     *     Switch : bool
+     *     TextField : string
+     *     TriState : TriState
+     *     Custom : custom object/value
+     */
+    enum Type {
+        kAction_Type,
+        kList_Type,
+        kSlider_Type,
+        kSwitch_Type,
+        kTriState_Type,
+        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[], 
+             SkEvent* evt);
+        ~Item() { delete fEvent; }
+        
+        SkEvent*    getEvent() const { return fEvent; }
+        int         getID() const { return fID; }
+        const char* getLabel() const { return fLabel.c_str(); }
+        const char* getSlotName() const { return fSlotName.c_str(); }
+        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 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 
+         * the associated event must be made prior to calling this method
+         */
+        void postEvent() const { (new SkEvent(*(fEvent)))->post(); }
+
+    private:
+        int             fID;
+        SkEvent*        fEvent;
+        SkString        fLabel;
+        SkString        fSlotName;
+        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 
+     * 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 
+     * defined for switches(toggling), tristates(cycle), and lists(cycle),
+     * 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 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); 
+    
+    /**
+     * 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 
+     * 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[], 
+                   SkEventSinkID target, int defaultIndex, const char[] ...);
+    int appendSlider(const char label[], const char slotName[], 
+                     SkEventSinkID target, SkScalar min, SkScalar max, 
+                     SkScalar defaultValue);
+    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 
+     * 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 
+     * mismatch or slotName is incorrect
+     */
+    static bool FindListIndex(const SkEvent& evt, const char slotName[], int* value);
+    static bool FindSliderValue(const SkEvent& evt, const char slotName[], SkScalar* value);
+    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/legacy/include/views/SkOSWindow_Android.h b/legacy/include/views/SkOSWindow_Android.h
new file mode 100644
index 0000000..bdce5d0
--- /dev/null
+++ b/legacy/include/views/SkOSWindow_Android.h
@@ -0,0 +1,38 @@
+
+/*
+ * Copyright 2011 Skia
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkOSWindow_Android_DEFINED
+#define SkOSWindow_Android_DEFINED
+
+#include "SkWindow.h"
+
+class SkIRect;
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void*) {}
+    ~SkOSWindow() {}
+    bool attachGL() { return true; }
+    void detachGL() {}
+    void presentGL() {}
+
+    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/legacy/include/views/SkOSWindow_Mac.h b/legacy/include/views/SkOSWindow_Mac.h
new file mode 100644
index 0000000..b09f1dd
--- /dev/null
+++ b/legacy/include/views/SkOSWindow_Mac.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 SkOSWindow_MacCocoa_DEFINED
+#define SkOSWindow_MacCocoa_DEFINED
+
+#include "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+    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();
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onEvent(const SkEvent& evt);
+    // overrides from SkWindow
+    virtual void onHandleInval(const SkIRect&);
+    // overrides from SkView
+    virtual void onAddMenu(const SkOSMenu*);
+    virtual void onUpdateMenu(const SkOSMenu*);
+    virtual void onSetTitle(const char[]);
+    
+private:
+    void*   fHWND;
+    bool    fInvalEventIsPending;
+    void*   fNotifier;
+    void*   fGLContext;
+    typedef SkWindow INHERITED;
+};
+
+#endif
diff --git a/legacy/include/views/SkOSWindow_SDL.h b/legacy/include/views/SkOSWindow_SDL.h
new file mode 100644
index 0000000..037de3b
--- /dev/null
+++ b/legacy/include/views/SkOSWindow_SDL.h
@@ -0,0 +1,45 @@
+
+/*
+ * 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_SDL_DEFINED
+#define SkOSWindow_SDL_DEFINED
+
+#include "SDL.h"
+#include "SkWindow.h"
+
+class SkGLCanvas;
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void* screen);
+    virtual ~SkOSWindow();
+
+    static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+    void handleSDLEvent(const SDL_Event& event);
+
+protected:
+    // overrides from SkWindow
+    virtual void onHandleInval(const SkIRect&);
+    // overrides from SkView
+    virtual void onAddMenu(const SkOSMenu*);
+    virtual void onSetTitle(const char[]);
+
+private:
+    SDL_Surface* fScreen;
+    SDL_Surface* fSurface;
+    SkGLCanvas* fGLCanvas;
+
+    void doDraw();
+
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/views/SkOSWindow_Unix.h b/legacy/include/views/SkOSWindow_Unix.h
new file mode 100644
index 0000000..d688fb5
--- /dev/null
+++ b/legacy/include/views/SkOSWindow_Unix.h
@@ -0,0 +1,70 @@
+
+/*
+ * 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_Unix_DEFINED
+#define SkOSWindow_Unix_DEFINED
+
+#include "SkWindow.h"
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+class SkBitmap;
+class SkEvent;
+
+struct SkUnixWindow {
+  Display* fDisplay;
+  Window fWin;
+  size_t fOSWin;
+  GC fGc;
+  GLXContext fGLContext;
+  bool fGLCreated;
+};
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void*);
+    ~SkOSWindow();
+
+    void* getHWND() const { return (void*)fUnixWindow.fWin; }
+    void* getDisplay() const { return (void*)fUnixWindow.fDisplay; }
+    void* getUnixWindow() const { return (void*)&fUnixWindow; }
+    void loop();
+    void post_linuxevent();
+    bool attachGL();
+    void detachGL();
+    void presentGL();
+
+    //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[]);
+
+private:
+    SkUnixWindow  fUnixWindow;
+    bool fGLAttached;
+
+    // Needed for GL
+    XVisualInfo* fVi;
+
+    void    doPaint();
+    void    mapWindowAndWait();
+
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/views/SkOSWindow_Win.h b/legacy/include/views/SkOSWindow_Win.h
new file mode 100644
index 0000000..bd6b4bd
--- /dev/null
+++ b/legacy/include/views/SkOSWindow_Win.h
@@ -0,0 +1,72 @@
+
+/*
+ * 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_Win_DEFINED
+#define SkOSWindow_Win_DEFINED
+
+#include "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void* hwnd);
+    virtual ~SkOSWindow();
+
+    void*   getHWND() const { return fHWND; }
+    void    setSize(int width, int height);
+    void    updateSize();
+
+    static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+    
+    bool attachGL();
+    void detachGL();
+    void presentGL();
+
+    bool attachD3D9();
+    void detachD3D9();
+    void presentD3D9();
+
+    void* d3d9Device() { return fD3D9Device; }
+
+    bool wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+    static bool QuitOnDeactivate(HWND hWnd);
+
+    enum {
+        SK_WM_SkEvent = WM_APP + 1000,
+        SK_WM_SkTimerID = 0xFFFF    // just need a non-zero value
+    };
+
+protected:
+    virtual bool quitOnDeactivate() { return true; }
+
+    // overrides from SkWindow
+    virtual void onHandleInval(const SkIRect&);
+    // overrides from SkView
+    virtual void onAddMenu(const SkOSMenu*);
+
+    virtual void onSetTitle(const char title[]);
+
+private:
+    void*               fHWND;
+    
+    void                doPaint(void* ctx);
+
+    void*               fHGLRC;
+
+    bool                fGLAttached;
+
+    void*               fD3D9Device;
+    bool                fD3D9Attached;
+
+    HMENU               fMBar;
+
+    typedef SkWindow INHERITED; 
+};
+
+#endif
+
diff --git a/legacy/include/views/SkOSWindow_iOS.h b/legacy/include/views/SkOSWindow_iOS.h
new file mode 100755
index 0000000..ff28484
--- /dev/null
+++ b/legacy/include/views/SkOSWindow_iOS.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 SkOSWindow_iOS_DEFINED
+#define SkOSWindow_iOS_DEFINED
+
+#include "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+    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();
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onEvent(const SkEvent& evt);
+    // overrides from SkWindow
+    virtual void onHandleInval(const SkIRect&);
+    // overrides from SkView
+    virtual void onAddMenu(const SkOSMenu*);
+    virtual void onUpdateMenu(const SkOSMenu*);
+    virtual void onSetTitle(const char[]);
+    
+private:
+    void*   fHWND;
+    bool    fInvalEventIsPending;
+    void*   fNotifier;
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_wxwidgets.h b/legacy/include/views/SkOSWindow_wxwidgets.h
similarity index 100%
rename from include/views/SkOSWindow_wxwidgets.h
rename to legacy/include/views/SkOSWindow_wxwidgets.h
diff --git a/include/views/SkProgressBarView.h b/legacy/include/views/SkProgressBarView.h
similarity index 100%
rename from include/views/SkProgressBarView.h
rename to legacy/include/views/SkProgressBarView.h
diff --git a/include/views/SkScrollBarView.h b/legacy/include/views/SkScrollBarView.h
similarity index 100%
rename from include/views/SkScrollBarView.h
rename to legacy/include/views/SkScrollBarView.h
diff --git a/legacy/include/views/SkStackViewLayout.h b/legacy/include/views/SkStackViewLayout.h
new file mode 100644
index 0000000..705ff74
--- /dev/null
+++ b/legacy/include/views/SkStackViewLayout.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 SkStackViewLayout_DEFINED
+#define SkStackViewLayout_DEFINED
+
+#include "SkView.h"
+
+class SkStackViewLayout : public SkView::Layout {
+public:
+    SkStackViewLayout();
+
+    enum Orient {
+        kHorizontal_Orient,
+        kVertical_Orient,
+
+        kOrientCount
+    };
+    Orient  getOrient() const { return (Orient)fOrient; }
+    void    setOrient(Orient);
+
+    void        getMargin(SkRect*) const;
+    void        setMargin(const SkRect&);
+
+    SkScalar    getSpacer() const { return fSpacer; }
+    void        setSpacer(SkScalar);
+
+    /** Controls the posititioning in the same direction as the orientation
+    */
+    enum Pack {
+        kStart_Pack,
+        kCenter_Pack,
+        kEnd_Pack,
+        
+        kPackCount
+    };
+    Pack    getPack() const { return (Pack)fPack; }
+    void    setPack(Pack);
+
+    /** Controls the posititioning at right angles to the orientation
+    */
+    enum Align {
+        kStart_Align,
+        kCenter_Align,
+        kEnd_Align,
+        kStretch_Align,
+
+        kAlignCount
+    };
+    Align   getAlign() const { return (Align)fAlign; }
+    void    setAlign(Align);
+
+    bool    getRound() const { return SkToBool(fRound); }
+    void    setRound(bool);
+
+protected:
+    virtual void onLayoutChildren(SkView* parent);
+    virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+    SkRect      fMargin;
+    SkScalar    fSpacer;
+    uint8_t     fOrient, fPack, fAlign, fRound;
+};
+
+class SkFillViewLayout : public SkView::Layout {
+public:
+            SkFillViewLayout();
+    void    getMargin(SkRect*) const;
+    void    setMargin(const SkRect&);
+
+protected:
+    // overrides;
+    virtual void onLayoutChildren(SkView* parent);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkRect  fMargin;
+    typedef SkView::Layout INHERITED;
+};
+
+#endif
+
diff --git a/legacy/include/views/SkSystemEventTypes.h b/legacy/include/views/SkSystemEventTypes.h
new file mode 100644
index 0000000..f0f2952
--- /dev/null
+++ b/legacy/include/views/SkSystemEventTypes.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 SkSystemEventTypes_DEFINED
+#define SkSystemEventTypes_DEFINED
+
+/*
+    The goal of these strings is two-fold:
+    1) make funny strings (containing at least one char < 32) to avoid colliding with "user" strings
+    2) keep them <= 4 bytes, so we can avoid an allocation in SkEvent::setType()
+*/
+#define SK_EventType_Delay      "\xd" "lay"
+#define SK_EventType_Inval      "nv" "\xa" "l"
+#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"
+
+#endif
diff --git a/legacy/include/views/SkTouchGesture.h b/legacy/include/views/SkTouchGesture.h
new file mode 100644
index 0000000..527065e
--- /dev/null
+++ b/legacy/include/views/SkTouchGesture.h
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkTouchGesture_DEFINED
+#define SkTouchGesture_DEFINED
+
+#include "SkTDArray.h"
+#include "SkMatrix.h"
+
+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;
+    double      fTime0;
+    bool        fActive;
+};
+
+class SkTouchGesture {
+public:
+    SkTouchGesture();
+    ~SkTouchGesture();
+
+    void touchBegin(void* owner, float x, float y);
+    void touchMoved(void* owner, float x, float y);
+    void touchEnd(void* owner);
+    void reset();
+
+    bool isActive() { return fFlinger.isActive(); }
+    void stop() { fFlinger.stop(); }
+
+    const SkMatrix& localM();
+    const SkMatrix& globalM() const { return fGlobalM; }
+
+private:
+    enum State {
+        kEmpty_State,
+        kTranslate_State,
+        kZoom_State,
+    };
+
+    struct Rec {
+        void*   fOwner;
+        float   fStartX, fStartY;
+        float   fPrevX, fPrevY;
+        float   fLastX, fLastY;
+        SkMSec  fPrevT, fLastT;
+    };
+    SkTDArray<Rec> fTouches;
+
+    State           fState;
+    SkMatrix        fLocalM, fGlobalM;
+    SkFlingState    fFlinger;
+    SkMSec          fLastUpT;
+    SkPoint         fLastUpP;
+
+
+    void flushLocalM();
+    int findRec(void* owner) const;
+    void appendNewRec(void* owner, float x, float y);
+    float computePinch(const Rec&, const Rec&);
+    float limitTotalZoom(float scale) const;
+    bool handleDblTap(float, float);
+};
+
+#endif
+
+
diff --git a/legacy/include/views/SkView.h b/legacy/include/views/SkView.h
new file mode 100644
index 0000000..a5349c2
--- /dev/null
+++ b/legacy/include/views/SkView.h
@@ -0,0 +1,382 @@
+
+/*
+ * 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 SkView_DEFINED
+#define SkView_DEFINED
+
+#include "SkEventSink.h"
+#include "SkRect.h"
+#include "SkDOM.h"
+#include "SkTDict.h"
+#include "SkMatrix.h"
+
+class SkCanvas;
+class SkLayerView;
+
+/** \class SkView
+
+    SkView is the base class for screen management. All widgets and controls inherit
+    from SkView.
+*/
+class SkView : public SkEventSink {
+public:
+    enum Flag_Shift {
+        kVisible_Shift,
+        kEnabled_Shift,
+        kFocusable_Shift,
+        kFlexH_Shift,
+        kFlexV_Shift,
+        kNoClip_Shift,
+
+        kFlagShiftCount
+    };
+    enum Flag_Mask {
+        kVisible_Mask   = 1 << kVisible_Shift,      //!< set if the view is visible
+        kEnabled_Mask   = 1 << kEnabled_Shift,      //!< set if the view is enabled
+        kFocusable_Mask = 1 << kFocusable_Shift,    //!< set if the view can receive focus
+        kFlexH_Mask     = 1 << kFlexH_Shift,        //!< set if the view's width is stretchable
+        kFlexV_Mask     = 1 << kFlexV_Shift,        //!< set if the view's height is stretchable
+        kNoClip_Mask    = 1 << kNoClip_Shift,        //!< set if the view is not clipped to its bounds
+
+        kAllFlagMasks   = (uint32_t)(0 - 1) >> (32 - kFlagShiftCount)
+    };
+
+                SkView(uint32_t flags = 0);
+    virtual     ~SkView();
+
+    /** Return the flags associated with the view
+    */
+    uint32_t    getFlags() const { return fFlags; }
+    /** Set the flags associated with the view
+    */
+    void        setFlags(uint32_t flags);
+
+    /** Helper that returns non-zero if the kVisible_Mask bit is set in the view's flags
+    */
+    int         isVisible() const { return fFlags & kVisible_Mask; }
+    int         isEnabled() const { return fFlags & kEnabled_Mask; }
+    int         isFocusable() const { return fFlags & kFocusable_Mask; }
+    int         isClipToBounds() const { return !(fFlags & kNoClip_Mask); }
+    /** Helper to set/clear the view's kVisible_Mask flag */
+    void        setVisibleP(bool);
+    void        setEnabledP(bool);
+    void        setFocusableP(bool);
+    void        setClipToBounds(bool);
+
+    /** Return the view's width */
+    SkScalar    width() const { return fWidth; }
+    /** Return the view's height */
+    SkScalar    height() const { return fHeight; }
+    /** Set the view's width and height. These must both be >= 0. This does not affect the view's loc */
+    void        setSize(SkScalar width, SkScalar height);
+    void        setSize(const SkPoint& size) { this->setSize(size.fX, size.fY); }
+    void        setWidth(SkScalar width) { this->setSize(width, fHeight); }
+    void        setHeight(SkScalar height) { this->setSize(fWidth, height); }
+    /** Return a rectangle set to [0, 0, width, height] */
+    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 
+        are applied in the following order:
+             canvas->translate(fLoc.fX, fLoc.fY);		
+             canvas->concat(fMatrix);
+    */
+    /** Return the view's left edge */
+    SkScalar    locX() const { return fLoc.fX; }
+    /** Return the view's top edge */
+    SkScalar    locY() const { return fLoc.fY; }
+    /** Set the view's left and top edge. This does not affect the view's size */
+    void        setLoc(SkScalar x, SkScalar y);
+    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 
+        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->concat(fMatrix);
+    */
+    const SkMatrix& getLocalMatrix() const { return fMatrix; }
+    void            setLocalMatrix(const SkMatrix& matrix);
+
+    /** Offset (move) the view by the specified dx and dy. This does not affect the view's size */
+    void        offset(SkScalar dx, SkScalar dy);
+
+    /** Call this to have the view draw into the specified canvas. */
+    virtual void draw(SkCanvas* canvas);
+
+    /** Call this to invalidate part of all of a view, requesting that the view's
+        draw method be called. The rectangle parameter specifies the part of the view
+        that should be redrawn. If it is null, it specifies the entire view bounds.
+    */
+    void        inval(SkRect* rectOrNull);
+
+    //  Focus management
+
+    SkView* getFocusView() const;
+    bool    hasFocus() const;
+
+    enum FocusDirection {
+        kNext_FocusDirection,
+        kPrev_FocusDirection,
+
+        kFocusDirectionCount
+    };
+    bool    acceptFocus();
+    SkView* moveFocus(FocusDirection);
+
+    //  Click handling
+
+    class Click {
+    public:
+        Click(SkView* target);
+        virtual ~Click();
+
+        const char* getType() const { return fType; }
+        bool        isType(const char type[]) const;
+        void        setType(const char type[]);     // does NOT make a copy of the string
+        void        copyType(const char type[]);    // makes a copy of the string
+
+        enum State {
+            kDown_State,
+            kMoved_State,
+            kUp_State
+        };
+        SkPoint     fOrig, fPrev, fCurr;
+        SkIPoint    fIOrig, fIPrev, fICurr;
+        State       fState;
+        void*       fOwner;
+    private:
+        SkEventSinkID   fTargetID;
+        char*           fType;
+        bool            fWeOwnTheType;
+
+        void resetType();
+
+        friend class SkView;
+    };
+    Click*  findClickHandler(SkScalar x, SkScalar y);
+
+    static void DoClickDown(Click*, int x, int y);
+    static void DoClickMoved(Click*, int x, int y);
+    static void DoClickUp(Click*, int x, int y);
+
+    /** 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
+        the event, null is returned.
+     */
+    SkView*     sendEventToParents(const SkEvent&);
+    /** Send the query to the view's parent, and its parent etc. until one of them
+        returns true from its onQuery call. This view is returned. If no parent handles
+        the query, null is returned.
+     */
+    SkView* sendQueryToParents(SkEvent*);
+
+    //  View hierarchy management
+
+    /** Return the view's parent, or null if it has none. This does not affect the parent's reference count. */
+    SkView*     getParent() const { return fParent; }
+    SkView*     attachChildToFront(SkView* child);
+    /** Attach the child view to this view, and increment the child's reference count. The child view is added
+        such that it will be drawn before all other child views.
+        The child view parameter is returned.
+    */
+    SkView*     attachChildToBack(SkView* child);
+    /** If the view has a parent, detach the view from its parent and decrement the view's reference count.
+        If the parent was the only owner of the view, this will cause the view to be deleted.
+    */
+    void        detachFromParent();
+    /** Attach the child view to this view, and increment the child's reference count. The child view is added
+        such that it will be drawn after all other child views.
+        The child view parameter is returned.
+    */
+    /** Detach all child views from this view. */
+    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); }
+    /** 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;
+
+    /** \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
+        next() returns null, there are no more child views.
+    */
+    class F2BIter {
+    public:
+        F2BIter(const SkView* parent);
+        SkView* next();
+    private:
+        SkView* fFirstChild, *fChild;
+    };
+
+    /** \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
+        next() returns null, there are no more child views.
+    */
+    class B2FIter {
+    public:
+        B2FIter(const SkView* parent);
+        SkView* next();
+    private:
+        SkView* fFirstChild, *fChild;
+    };
+
+    /** \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:
+        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*);
+    };
+    /** Return the artist attached to this view (or null). The artist's reference
+        count is not affected.
+    */
+    Artist* getArtist() const;
+    /** Attach the specified artist (or null) to the view, replacing any existing
+        artist. If the new artist is not null, its reference count is incremented.
+        The artist parameter is returned.
+    */
+    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:
+        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*);
+    };
+
+    /** Return the layout attached to this view (or null). The layout's reference
+        count is not affected.
+    */
+    Layout* getLayout() const;
+    /** Attach the specified layout (or null) to the view, replacing any existing
+        layout. If the new layout is not null, its reference count is incremented.
+        The layout parameter is returned.
+    */
+    Layout* setLayout(Layout*, bool invokeLayoutNow = true);
+    /** If a layout is attached to this view, call its layoutChildren() method
+    */
+    void    invokeLayout();
+
+    /** Call this to initialize this view based on the specified XML node
+    */
+    void    inflate(const SkDOM& dom, const SkDOM::Node* node);
+    /** After a view hierarchy is inflated, this may be called with a dictionary
+        containing pairs of <name, view*>, where the name string was the view's
+        "id" attribute when it was inflated.
+
+        This will call the virtual onPostInflate for this view, and the recursively
+        call postInflate on all of the view's children.
+    */
+    void    postInflate(const SkTDict<SkView*>& ids);
+
+    SkDEBUGCODE(void dump(bool recurse) const;)
+
+protected:
+    /** Override this to draw inside the view. Be sure to call the inherited version too */
+    virtual void    onDraw(SkCanvas*);
+    /** Override this to be notified when the view's size changes. Be sure to call the inherited version too */
+    virtual void    onSizeChange();
+    /** Override this if you want to handle an inval request from this view or one of its children.
+        Tyically this is only overridden by the by the "window". If your subclass does handle the
+        request, return true so the request will not continue to propogate to the parent.
+    */
+    virtual bool    handleInval(const SkRect*);
+    //! called once before all of the children are drawn (or clipped/translated)
+    virtual SkCanvas* beforeChildren(SkCanvas* c) { return c; }
+    //! called once after all of the children are drawn (or clipped/translated)
+    virtual void afterChildren(SkCanvas* orig) {}
+
+    //! called right before this child's onDraw is called
+    virtual void beforeChild(SkView* child, SkCanvas* canvas) {}
+    //! called right after this child's onDraw is called
+    virtual void afterChild(SkView* child, SkCanvas* canvas) {}
+
+    /** Override this if you might handle the click
+    */
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    /** 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);
+    /** Override this to track clicks, returning true as long as you want to track
+        the pen/mouse.
+    */
+    virtual bool    onClick(Click*);
+    /** Override this to initialize your subclass from the XML node. Be sure to call the inherited version too */
+    virtual void    onInflate(const SkDOM& dom, const SkDOM::Node* node);
+    /** Override this if you want to perform post initialization work based on the ID dictionary built
+        during XML parsing. Be sure to call the inherited version too.
+    */
+    virtual void    onPostInflate(const SkTDict<SkView*>&);
+
+public:
+    // default action is to inval the view
+    virtual void    onFocusChange(bool gainFocusP);
+protected:
+
+    // override these if you're acting as a layer/host
+    virtual bool    onGetFocusView(SkView**) const { return false; }
+    virtual bool    onSetFocusView(SkView*) { return false; }
+
+private:
+    SkScalar    fWidth, fHeight;
+    SkMatrix    fMatrix;
+    SkPoint     fLoc;
+    SkView*     fParent;
+    SkView*     fFirstChild;
+    SkView*     fNextSibling;
+    SkView*     fPrevSibling;
+    uint8_t     fFlags;
+    uint8_t     fContainsFocus;
+
+    friend class B2FIter;
+    friend class F2BIter;
+    
+    friend class SkLayerView;
+
+    bool    setFocusView(SkView* fvOrNull);
+    SkView* acceptFocus(FocusDirection);
+    void    detachFromParent_NoLayout();
+    /** Compute the matrix to transform view-local coordinates into global ones */
+    void    localToGlobal(SkMatrix* matrix) const;
+};
+
+#endif
+
diff --git a/legacy/include/views/SkViewInflate.h b/legacy/include/views/SkViewInflate.h
new file mode 100644
index 0000000..7282091
--- /dev/null
+++ b/legacy/include/views/SkViewInflate.h
@@ -0,0 +1,72 @@
+
+/*
+ * 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 SkViewInflate_DEFINED
+#define SkViewInflate_DEFINED
+
+#include "SkDOM.h"
+#include "SkTDict.h"
+#include "SkEvent.h"
+
+class SkView;
+
+class SkViewInflate {
+public: 
+            SkViewInflate();
+    virtual ~SkViewInflate();
+
+    /** Return the tree of inflated views. If root is null, create the root element
+        as a view, otherwise assume root is that view, and just "inflate" it.
+
+        Returns null if the tree cannot be built.
+    */
+    SkView* inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root = NULL);
+    SkView* inflate(const char xml[], size_t len, SkView* root = NULL);
+
+    /** Given an id attribute value, return the corresponding view, or null
+        if no match is found.
+    */
+    SkView* findViewByID(const char id[]) const;
+    
+    SkDEBUGCODE(void dump() const;)
+
+protected:
+    /*  Override this in your subclass to handle instantiating views
+        Call the inherited version for nodes you don't recognize.
+
+        Do not call "inflate" on the view, just return it. This will
+        get called automatically after createView returns.
+    */
+    virtual SkView* createView(const SkDOM& dom, const SkDOM::Node* node);
+    /** Base implementation calls view->inflate(dom, node). Subclasses may override this
+        to perform additional initializations to view, either before or after calling
+        the inherited version.
+    */
+    virtual void inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    enum {
+        kMinIDStrAlloc = 64
+    };
+    SkTDict<SkView*> fIDs;
+
+    struct IDStr {
+        SkView* fView;
+        char*   fStr;
+    };
+    SkTDArray<IDStr>    fListenTo, fBroadcastTo;
+    SkChunkAlloc        fStrings;
+
+    void addIDStr(SkTDArray<IDStr>* list, SkView*, const char* str);
+
+    void    rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent);
+};
+
+#endif
+
diff --git a/legacy/include/views/SkWidget.h b/legacy/include/views/SkWidget.h
new file mode 100644
index 0000000..c3b4530
--- /dev/null
+++ b/legacy/include/views/SkWidget.h
@@ -0,0 +1,469 @@
+
+/*
+ * 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 SkWidget_DEFINED
+#define SkWidget_DEFINED
+
+#include "SkView.h"
+#include "SkBitmap.h"
+#include "SkDOM.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SkWidget : public SkView {
+public:
+    SkWidget(uint32_t flags = 0) : SkView(flags | kFocusable_Mask | kEnabled_Mask) {}
+
+    /** Call this to post the widget's event to its listeners */
+    void    postWidgetEvent();
+
+    static void Init();
+    static void Term();
+protected:
+    // override to add slots to an event before posting
+    virtual void prepareWidgetEvent(SkEvent*);
+    virtual void onEnabledChange();
+
+    // <event ...> to initialize the event from XML
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkEvent fEvent;
+    typedef SkView INHERITED;
+};
+
+class SkHasLabelWidget : public SkWidget {
+public:
+    SkHasLabelWidget(uint32_t flags = 0) : SkWidget(flags) {}
+
+    size_t  getLabel(SkString* label = NULL) const;
+    size_t  getLabel(char lable[] = NULL) const;
+    void    setLabel(const SkString&);
+    void    setLabel(const char label[]);
+    void    setLabel(const char label[], size_t len);
+
+protected:
+    // called when the label changes
+    virtual void onLabelChange();
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    SkString    fLabel;
+    typedef SkWidget INHERITED;
+};
+
+class SkButtonWidget : public SkHasLabelWidget {
+public:
+    SkButtonWidget(uint32_t flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {}
+
+    enum State {
+        kOff_State,     //!< XML: buttonState="off"
+        kOn_State,      //!< XML: buttonState="on"
+        kUnknown_State  //!< XML: buttonState="unknown"
+    };
+    State   getButtonState() const { return fState; }
+    void    setButtonState(State);
+
+protected:
+    /** called when the label changes. default behavior is to inval the widget */
+    virtual void onButtonStateChange();
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    State   fState;
+    typedef SkHasLabelWidget INHERITED;
+};
+
+class SkPushButtonWidget : public SkButtonWidget {
+public:
+    SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {}
+
+protected:
+    virtual bool onEvent(const SkEvent&);
+    virtual void onDraw(SkCanvas*);
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    virtual bool onClick(Click* click);
+
+private:
+    typedef SkButtonWidget INHERITED;
+};
+
+class SkCheckBoxWidget : public SkButtonWidget {
+public:
+    SkCheckBoxWidget(uint32_t flags = 0);
+
+protected:
+    virtual bool onEvent(const SkEvent&);
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    typedef SkButtonWidget INHERITED;
+};
+
+#include "SkTextBox.h"
+
+class SkStaticTextView : public SkView {
+public:
+            SkStaticTextView(uint32_t flags = 0);
+    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 SkBitmapView : public SkView {
+public:
+            SkBitmapView(uint32_t flags = 0);
+    virtual ~SkBitmapView();
+
+    bool    getBitmap(SkBitmap*) const;
+    void    setBitmap(const SkBitmap*, bool viewOwnsPixels);
+    bool    loadBitmapFromFile(const char path[]);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+    SkBitmap    fBitmap;
+    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:
+    void    getLabel(SkString*) const;
+    void    setLabel(const SkString&);
+    void    setLabel(const char label[]);
+
+protected:
+    SkString    fLabel;
+
+    // called when the label changes
+    virtual void onLabelChange();
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+};
+
+class SkPushButtonView : public SkHasLabelView {
+public:
+    SkPushButtonView(uint32_t flags = 0);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+};
+
+class SkCheckBoxView : public SkHasLabelView {
+public:
+    SkCheckBoxView(uint32_t flags = 0);
+
+    enum State {
+        kOff_State,
+        kOn_State,
+        kMaybe_State
+    };
+    State   getState() const { return fState; }
+    void    setState(State);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    State   fState;
+};
+
+class SkProgressView : public SkView {
+public:
+    SkProgressView(uint32_t flags = 0);
+    virtual ~SkProgressView();
+
+    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 void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    uint16_t    fValue, fMax;
+    SkShader*   fOnShader, *fOffShader;
+    SkInterpolator* fInterp;
+    bool fDoInterp;
+
+    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:
+    virtual int countRows() = 0;
+    virtual void getRow(int index, SkString* left, SkString* right) = 0;
+    virtual SkEvent* getEvent(int index);
+
+    static SkListSource* CreateFromDir(const char path[], const char suffix[],
+                                        const char targetPrefix[]);
+    static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node);
+};
+
+class SkListView : public SkWidgetView {
+public:
+            SkListView(uint32_t flags = 0);
+    virtual ~SkListView();
+
+    SkScalar    getRowHeight() const { return fRowHeight; }
+    void        setRowHeight(SkScalar);
+
+    /** 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);
+
+    void    moveSelectionUp();
+    void    moveSelectionDown();
+
+    enum Attr {
+        kBG_Attr,
+        kNormalText_Attr,
+        kHiliteText_Attr,
+        kHiliteCell_Attr,
+        kAttrCount
+    };
+    SkPaint&    paint(Attr);
+
+    SkListSource*   getListSource() const { return fSource; }
+    SkListSource*   setListSource(SkListSource*);
+
+#if 0
+    enum Action {
+        kSelectionChange_Action,
+        kSelectionPicked_Action,
+        kActionCount
+    };
+    /** If event is not null, it is retained by the view, and a copy
+        of the event will be posted to its listeners when the specified
+        action occurs. If event is null, then no event will be posted for
+        the specified action.
+    */
+    void    setActionEvent(Action, SkEvent* event);
+#endif
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onSizeChange();
+    virtual bool onEvent(const SkEvent&);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkPaint         fPaint[kAttrCount];
+    SkListSource*   fSource;
+    SkScalar        fRowHeight;
+    int             fCurrIndex;     // logical index
+    int             fScrollIndex;   // logical index of top-most visible row
+    int             fVisibleRowCount;
+    SkString*       fStrCache;
+
+    void    dirtyStrCache();
+    void    ensureStrCache(int visibleCount);
+
+    int     logicalToVisualIndex(int index) const { return index - fScrollIndex; }
+    void    invalSelection();
+    bool    getRowRect(int index, SkRect*) const;
+    void    ensureSelectionIsVisible();
+
+    typedef SkWidgetView INHERITED;
+};
+
+//////////////////////////////////////////////////////////
+
+class SkGridView : public SkWidgetView {
+public:
+            SkGridView(uint32_t flags = 0);
+    virtual ~SkGridView();
+
+    void    getCellSize(SkPoint*) const;
+    void    setCellSize(SkScalar x, SkScalar y);
+
+    /** Return the index of the selected item, or -1 if none
+    */
+    int     getSelection() const { return fCurrIndex; }
+    /** Set the index of the selected row, or -1 for none
+    */
+    void    setSelection(int);
+
+    void    moveSelectionUp();
+    void    moveSelectionDown();
+
+    enum Attr {
+        kBG_Attr,
+        kHiliteCell_Attr,
+        kAttrCount
+    };
+    SkPaint&    paint(Attr);
+
+    SkListSource*   getListSource() const { return fSource; }
+    SkListSource*   setListSource(SkListSource*);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onSizeChange();
+    virtual bool onEvent(const SkEvent&);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkView*         fScrollBar;
+    SkPaint         fPaint[kAttrCount];
+    SkListSource*   fSource;
+    int             fCurrIndex;     // logical index
+
+    SkPoint         fCellSize;
+    SkIPoint        fVisibleCount;
+
+    int     logicalToVisualIndex(int index) const { return index; }
+    void    invalSelection();
+    bool    getCellRect(int index, SkRect*) const;
+    void    ensureSelectionIsVisible();
+
+    typedef SkWidgetView INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkWidgetViews.h b/legacy/include/views/SkWidgetViews.h
similarity index 100%
rename from include/views/SkWidgetViews.h
rename to legacy/include/views/SkWidgetViews.h
diff --git a/legacy/include/views/SkWindow.h b/legacy/include/views/SkWindow.h
new file mode 100644
index 0000000..c72163c
--- /dev/null
+++ b/legacy/include/views/SkWindow.h
@@ -0,0 +1,122 @@
+
+/*
+ * 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 SkWindow_DEFINED
+#define SkWindow_DEFINED
+
+#include "SkView.h"
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkTDArray.h"
+
+#ifdef SK_BUILD_FOR_WINCEx
+    #define SHOW_FPS
+#endif
+//#define USE_GX_SCREEN
+
+class SkCanvas;
+
+class SkOSMenu;
+
+class SkWindow : public SkView {
+public:
+            SkWindow();
+    virtual ~SkWindow();
+
+    const SkBitmap& getBitmap() const { return fBitmap; }
+
+    void    setConfig(SkBitmap::Config);
+    void    resize(int width, int height, SkBitmap::Config config = SkBitmap::kNo_Config);
+    void    eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+    void    eraseRGB(U8CPU r, U8CPU g, U8CPU b);
+
+    bool    isDirty() const { return !fDirtyRgn.isEmpty(); }
+    bool    update(SkIRect* updateArea, SkCanvas* = NULL);
+    // 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    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[]);
+
+    const SkMatrix& getMatrix() const { return fMatrix; }
+    void    setMatrix(const SkMatrix&);
+    void    preConcat(const SkMatrix&);
+    void    postConcat(const SkMatrix&);
+
+    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);
+    // called if part of our bitmap is invalidated
+    virtual void onHandleInval(const SkIRect&);
+    virtual bool onHandleChar(SkUnichar);
+    virtual bool onHandleKey(SkKey);
+    virtual bool onHandleKeyUp(SkKey);
+    virtual void onAddMenu(const SkOSMenu*) {};
+    virtual void onUpdateMenu(const SkOSMenu*) {};
+    virtual void onSetTitle(const char title[]) {}
+
+    // overrides from SkView
+    virtual bool handleInval(const SkRect*);
+    virtual bool onGetFocusView(SkView** focus) const;
+    virtual bool onSetFocusView(SkView* focus);
+
+private:
+    SkBitmap::Config    fConfig;
+    SkBitmap    fBitmap;
+    SkRegion    fDirtyRgn;
+
+    SkTDArray<Click*>       fClicks; // to track clicks
+
+    SkTDArray<SkOSMenu*>    fMenus;
+
+    SkView* fFocusView;
+    bool    fWaitingOnInval;
+    
+    SkString    fTitle;
+    SkMatrix    fMatrix;
+
+    typedef SkView INHERITED;
+};
+
+///////////////////////////////////////////////////////////
+
+#ifdef SK_USE_WXWIDGETS
+    #include "SkOSWindow_wxwidgets.h"
+#elif defined(SK_BUILD_FOR_MAC)
+    #include "SkOSWindow_Mac.h"
+#elif defined(SK_BUILD_FOR_WIN)
+    #include "SkOSWindow_Win.h"
+#elif defined(SK_BUILD_FOR_ANDROID)
+    #include "SkOSWindow_Android.h"
+#elif defined(SK_BUILD_FOR_UNIX)
+  #include "SkOSWindow_Unix.h"
+#elif defined(SK_BUILD_FOR_SDL)
+    #include "SkOSWindow_SDL.h"
+#elif defined(SK_BUILD_FOR_IOS)
+    #include "SkOSWindow_iOS.h"
+#endif
+
+#endif
+
diff --git a/legacy/include/xml/SkBML_WXMLParser.h b/legacy/include/xml/SkBML_WXMLParser.h
new file mode 100644
index 0000000..e16b95c
--- /dev/null
+++ b/legacy/include/xml/SkBML_WXMLParser.h
@@ -0,0 +1,47 @@
+
+/*
+ * 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 SkBML_WXMLParser_DEFINED
+#define SkBML_WXMLParser_DEFINED
+
+#include "SkString.h"
+#include "SkXMLParser.h"
+
+class SkStream;
+class SkWStream;
+
+class BML_WXMLParser : public SkXMLParser {
+public:
+    BML_WXMLParser(SkWStream& writer);
+    virtual ~BML_WXMLParser();
+    static void Write(SkStream& s, const char filename[]);
+  
+  /** @cond UNIT_TEST */
+  SkDEBUGCODE(static void UnitTest();)
+  /** @endcond */  
+private:
+    virtual bool onAddAttribute(const char name[], const char value[]);
+    virtual bool onEndElement(const char name[]);
+    virtual bool onStartElement(const char name[]);
+    BML_WXMLParser& operator=(const BML_WXMLParser& src);
+#ifdef SK_DEBUG
+    int fElemsCount, fElemsReused;
+    int fAttrsCount, fNamesReused, fValuesReused;
+#endif
+    SkWStream&  fWriter;
+    char*       fElems[256];
+    char*       fAttrNames[256];
+    char*       fAttrValues[256];
+
+    // important that these are U8, so we get automatic wrap-around
+    U8  fNextElem, fNextAttrName, fNextAttrValue;
+};
+
+#endif // SkBML_WXMLParser_DEFINED
+
diff --git a/legacy/include/xml/SkBML_XMLParser.h b/legacy/include/xml/SkBML_XMLParser.h
new file mode 100644
index 0000000..7a8d6a1
--- /dev/null
+++ b/legacy/include/xml/SkBML_XMLParser.h
@@ -0,0 +1,32 @@
+
+/*
+ * 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 SkBML_XMLParser_DEFINED
+#define SkBML_XMLParser_DEFINED
+
+class SkStream;
+class SkWStream;
+class SkXMLParser;
+class SkXMLWriter;
+
+class BML_XMLParser {
+public:
+    /** Read the byte XML stream and write the decompressed XML.
+    */
+    static void Read(SkStream& s, SkXMLWriter& writer);
+    /** Read the byte XML stream and write the decompressed XML into a writable stream.
+    */
+    static void Read(SkStream& s, SkWStream& output);
+    /** Read the byte XML stream and write the decompressed XML into an XML parser.
+    */
+    static void Read(SkStream& s, SkXMLParser& output);
+};
+
+#endif // SkBML_XMLParser_DEFINED
+
diff --git a/legacy/include/xml/SkDOM.h b/legacy/include/xml/SkDOM.h
new file mode 100644
index 0000000..60145c8
--- /dev/null
+++ b/legacy/include/xml/SkDOM.h
@@ -0,0 +1,92 @@
+
+/*
+ * 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 SkDOM_DEFINED
+#define SkDOM_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkScalar.h"
+#include "SkTemplates.h"
+
+struct SkDOMNode;
+struct SkDOMAttr;
+
+class SkDOM {
+public:
+    SkDOM();
+    ~SkDOM();
+
+    typedef SkDOMNode Node;
+    typedef SkDOMAttr Attr;
+
+    /** Returns null on failure
+    */
+    const Node* build(const char doc[], size_t len);
+    const Node* copy(const SkDOM& dom, const Node* node);
+
+    const Node* getRootNode() const;
+
+    enum Type {
+        kElement_Type,
+        kText_Type
+    };
+    Type    getType(const Node*) const;
+
+    const char* getName(const Node*) const;
+    const Node* getFirstChild(const Node*, const char elem[] = NULL) const;
+    const Node* getNextSibling(const Node*, const char elem[] = NULL) const;
+
+    const char* findAttr(const Node*, const char attrName[]) const;
+    const Attr* getFirstAttr(const Node*) const;
+    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;
+
+    // helpers for calling SkParse
+    bool findS32(const Node*, const char name[], int32_t* value) const;
+    bool findScalars(const Node*, const char name[], SkScalar value[], int count) const;
+    bool findHex(const Node*, const char name[], uint32_t* value) const;
+    bool findBool(const Node*, const char name[], bool*) const;
+    int  findList(const Node*, const char name[], const char list[]) const;
+
+    bool findScalar(const Node* node, const char name[], SkScalar value[]) const
+    {
+        return this->findScalars(node, name, value, 1);
+    }
+
+    bool hasAttr(const Node*, const char name[], const char value[]) const;
+    bool hasS32(const Node*, const char name[], int32_t value) const;
+    bool hasScalar(const Node*, const char name[], SkScalar value) const;
+    bool hasHex(const Node*, const char name[], uint32_t value) const;
+    bool hasBool(const Node*, const char name[], bool value) const;
+
+    class AttrIter {
+    public:
+        AttrIter(const class SkDOM&, const Node*);
+        const char* next(const char** value);
+    private:
+        const Attr* fAttr;
+        const Attr* fStop;
+    };
+
+    SkDEBUGCODE(void dump(const Node* node = NULL, int tabLevel = 0) const;)
+    SkDEBUGCODE(static void UnitTest();)
+
+private:
+    SkChunkAlloc    fAlloc;
+    Node*           fRoot;
+    friend class AttrIter;
+    friend class SkDOMParser;
+};
+
+#endif
+
diff --git a/legacy/include/xml/SkJS.h b/legacy/include/xml/SkJS.h
new file mode 100644
index 0000000..645e03b
--- /dev/null
+++ b/legacy/include/xml/SkJS.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.
+ */
+
+
+#include "SkTypes.h"
+#include "SkWindow.h"
+
+extern "C" {
+    typedef long JSWord;
+    typedef JSWord jsword;
+    typedef jsword  jsval;
+    typedef struct JSRuntime JSRuntime;
+    typedef struct JSContext JSContext;
+    typedef struct JSObject JSObject;
+}
+
+class SkString;
+
+class SkJS : public SkOSWindow {
+public:
+    SkJS(void* hwnd);
+    ~SkJS();
+    SkBool EvaluateScript(const char* script, jsval* rVal);
+    SkBool ValueToString(jsval value, SkString* string);
+#ifdef SK_DEBUG
+    static void Test(void* hwnd);
+#endif
+protected:
+    void InitializeDisplayables(const SkBitmap& , JSContext *, JSObject *, JSObject *);
+    void DisposeDisplayables();
+    JSRuntime *fRuntime;
+    JSContext *fContext;
+    JSObject *fGlobal;
+};
+
diff --git a/legacy/include/xml/SkXMLParser.h b/legacy/include/xml/SkXMLParser.h
new file mode 100644
index 0000000..bd8c2f1
--- /dev/null
+++ b/legacy/include/xml/SkXMLParser.h
@@ -0,0 +1,155 @@
+
+/*
+ * 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 SkXMLParser_DEFINED
+#define SkXMLParser_DEFINED
+
+#include "SkString.h"
+
+class SkStream;
+
+class SkDOM;
+struct SkDOMNode;
+
+class SkXMLParserError {
+public:
+    enum ErrorCode {
+        kNoError,
+        kEmptyFile,
+        kUnknownElement,
+        kUnknownAttributeName,
+        kErrorInAttributeValue,
+        kDuplicateIDs,
+        kUnknownError
+    };
+
+    SkXMLParserError();
+    virtual ~SkXMLParserError();
+    ErrorCode getErrorCode() const { return fCode; }
+    virtual void getErrorString(SkString* str) const;
+    int getLineNumber() const { return fLineNumber; }
+    int getNativeCode() const { return fNativeCode; }
+    bool hasError() const { return fCode != kNoError || fNativeCode != -1; }
+    bool hasNoun() const { return fNoun.size() > 0; }
+    void reset();
+    void setCode(ErrorCode code) { fCode = code; }
+    void setNoun(const SkString& str) { fNoun.set(str); }
+    void setNoun(const char* ch)  { fNoun.set(ch); }
+    void setNoun(const char* ch, size_t len) { fNoun.set(ch, len); }
+protected:
+    ErrorCode fCode;
+private:
+    int fLineNumber;
+    int fNativeCode;
+    SkString fNoun;
+    friend class SkXMLParser;
+};
+
+class SkXMLParser {
+public:
+            SkXMLParser(SkXMLParserError* parserError = NULL);
+    virtual ~SkXMLParser();
+
+    /** Returns true for success
+    */
+    bool parse(const char doc[], size_t len);
+    bool parse(SkStream& docStream);
+    bool parse(const SkDOM&, const SkDOMNode*);
+
+    static void GetNativeErrorString(int nativeErrorCode, SkString* str);
+
+protected:
+    // override in subclasses; return true to stop parsing
+    virtual bool onStartElement(const char elem[]);
+    virtual bool onAddAttribute(const char name[], const char value[]);
+    virtual bool onEndElement(const char elem[]);
+    virtual bool onText(const char text[], int len);
+
+public:
+    // public for ported implementation, not meant for clients to call
+    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); 
+    void* fParser;
+protected:
+    SkXMLParserError* fError;
+private:
+    void reportError(void* parser);
+};
+
+#if 0
+class SkXMLPullParser {
+public:
+            SkXMLPullParser();
+    explicit SkXMLPullParser(SkStream*);
+    virtual ~SkXMLPullParser();
+
+    SkStream*   getStream() const { return fStream; }
+    SkStream*   setStream(SkStream* stream);
+
+    enum EventType {
+        ERROR = -1,
+        START_DOCUMENT,
+        END_DOCUMENT,
+        START_TAG,
+        END_TAG,
+        TEXT,
+        CDSECT,
+        ENTITY_REF,
+        IGNORABLE_WHITESPACE,
+        PROCESSING_INSTRUCTION,
+        COMMENT,
+        DOCDECL
+    };
+
+    EventType   nextToken();
+    EventType   getEventType() const { return fCurr.fEventType; }
+
+    struct AttrInfo {
+        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);
+
+public:
+    struct Curr {
+        EventType   fEventType;
+        const char* fName;
+        AttrInfo*   fAttrInfos;
+        int         fAttrInfoCount;
+        bool        fIsWhitespace;
+    };
+
+private:
+    // implemented in the porting layer
+    bool        onInit();   // return false on failure
+    EventType   onNextToken();
+    void        onExit();
+    
+    SkStream*   fStream;
+    Curr        fCurr;
+    int         fDepth;
+    
+    struct Impl;
+    Impl*   fImpl;
+};
+#endif
+
+#endif
diff --git a/legacy/include/xml/SkXMLWriter.h b/legacy/include/xml/SkXMLWriter.h
new file mode 100644
index 0000000..4e0eda1
--- /dev/null
+++ b/legacy/include/xml/SkXMLWriter.h
@@ -0,0 +1,86 @@
+
+/*
+ * 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 SkXMLWriter_DEFINED
+#define SkXMLWriter_DEFINED
+
+#include "SkTDArray.h"
+#include "SkString.h"
+#include "SkDOM.h"
+
+class SkWStream;
+class SkXMLParser;
+
+class SkXMLWriter {
+public:
+            SkXMLWriter(bool doEscapeMarkup = true);
+    virtual ~SkXMLWriter();
+
+    void    addS32Attribute(const char name[], int32_t value);
+    void    addAttribute(const char name[], const char value[]);
+    void    addAttributeLen(const char name[], const char value[], size_t length);
+    void    addHexAttribute(const char name[], uint32_t value, int minDigits = 0);
+    void    addScalarAttribute(const char name[], SkScalar value);
+    void    endElement() { this->onEndElement(); }
+    void    startElement(const char elem[]);
+    void    startElementLen(const char elem[], size_t length);
+    void    writeDOM(const SkDOM&, const SkDOM::Node*, bool skipRoot);
+    void    flush();
+    virtual void writeHeader();
+
+protected:
+    virtual void onStartElementLen(const char elem[], size_t length) = 0;
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length) = 0;
+    virtual void onEndElement() = 0;
+
+    struct Elem {
+        SkString    fName;
+        bool        fHasChildren;
+    };
+    void doEnd(Elem* elem);
+    bool doStart(const char name[], size_t length);
+    Elem* getEnd();
+    const char* getHeader();
+    SkTDArray<Elem*> fElems;
+
+private:
+    bool fDoEscapeMarkup;
+    // illegal
+    SkXMLWriter& operator=(const SkXMLWriter&);
+};
+
+class SkXMLStreamWriter : public SkXMLWriter {
+public:
+    SkXMLStreamWriter(SkWStream*);
+    virtual ~SkXMLStreamWriter();
+    virtual void    writeHeader();
+    SkDEBUGCODE(static void UnitTest();)
+protected:
+    virtual void onStartElementLen(const char elem[], size_t length);
+    virtual void onEndElement();
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length);
+private:
+    SkWStream&      fStream;
+};
+
+class SkXMLParserWriter : public SkXMLWriter {
+public:
+    SkXMLParserWriter(SkXMLParser*);
+    virtual ~SkXMLParserWriter();
+protected:
+    virtual void onStartElementLen(const char elem[], size_t length);
+    virtual void onEndElement();
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length);
+private:
+    SkXMLParser&        fParser;
+};
+
+
+#endif
+
diff --git a/legacy/src/animator/SkAnimate.h b/legacy/src/animator/SkAnimate.h
new file mode 100644
index 0000000..b7018b9
--- /dev/null
+++ b/legacy/src/animator/SkAnimate.h
@@ -0,0 +1,35 @@
+
+/*
+ * 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 SkAnimate_DEFINED
+#define SkAnimate_DEFINED
+
+#include "SkAnimateBase.h"
+#include "SkDisplayType.h"
+#include "SkIntArray.h"
+#include "SkUtils.h"
+
+class SkAnimate : public SkAnimateBase {
+    DECLARE_MEMBER_INFO(Animate);
+    SkAnimate();
+    virtual ~SkAnimate();
+    virtual int components();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& maker);
+protected:
+    bool resolveCommon(SkAnimateMaker& );
+    int fComponents;
+private:
+    typedef SkAnimateBase INHERITED;
+};
+
+#endif // SkAnimateField_DEFINED
+
diff --git a/legacy/src/animator/SkAnimate3DSchema.xsd b/legacy/src/animator/SkAnimate3DSchema.xsd
new file mode 100644
index 0000000..5063b75
--- /dev/null
+++ b/legacy/src/animator/SkAnimate3DSchema.xsd
@@ -0,0 +1,39 @@
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

+	xmlns:Sk="http://www.skia.com/schema/SkAnimateSchema.xsd"

+	targetNamespace="urn:skia3D" xmlns:Sk3D="urn:skia3D">

+

+	<xs:simpleType name="Patch" >

+		<xs:restriction base="xs:string" >

+		</xs:restriction>

+	</xs:simpleType>

+

+	<xs:simpleType name="Point" >

+		<xs:restriction base="xs:string" >

+			<xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *[ ,] *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)){2}" /> 

+		</xs:restriction>

+	</xs:simpleType>

+

+	<xs:element name="camera">

+		<xs:complexType >

+			<xs:attribute name="axis" type="Sk3D:Point" />

+			<xs:attribute name="hackHeight" type="Sk:Float" />

+			<xs:attribute name="hackWidth" type="Sk:Float" />

+			<xs:attribute name="location" type="Sk3D:Point" />

+			<xs:attribute name="observer" type="Sk3D:Point" />

+			<xs:attribute name="patch" type="Sk3D:Patch" />

+			<xs:attribute name="zenith" type="Sk3D:Point" />

+			<xs:attribute name="id" type="xs:ID" />

+		</xs:complexType>

+	</xs:element>

+

+	<xs:element name="patch">

+		<xs:complexType >

+			<xs:attribute name="origin" type="Sk3D:Point" />

+			<xs:attribute name="rotateDegrees" type="Sk:MemberFunction" />

+			<xs:attribute name="u" type="Sk3D:Point" />

+			<xs:attribute name="v" type="Sk3D:Point" />

+			<xs:attribute name="id" type="xs:ID" />

+		</xs:complexType>

+	</xs:element>

+

+</xs:schema>

diff --git a/legacy/src/animator/SkAnimate3DSchema.xsx b/legacy/src/animator/SkAnimate3DSchema.xsx
new file mode 100644
index 0000000..ceb7d89
--- /dev/null
+++ b/legacy/src/animator/SkAnimate3DSchema.xsx
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!--This file is auto-generated by the XML Schema Designer. It holds layout information for components on the designer surface.-->

+<XSDDesignerLayout />
diff --git a/legacy/src/animator/SkAnimateActive.cpp b/legacy/src/animator/SkAnimateActive.cpp
new file mode 100644
index 0000000..4177aa0
--- /dev/null
+++ b/legacy/src/animator/SkAnimateActive.cpp
@@ -0,0 +1,506 @@
+
+/*
+ * 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 "SkAnimateActive.h"
+#include "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+#include "SkDrawGroup.h"
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+
+// SkActive holds array of interpolators
+
+SkActive::SkActive(SkApply& apply, SkAnimateMaker& maker) : fApply(apply),
+    fMaxTime(0), fMaker(maker), fDrawIndex(0), fDrawMax(0) {
+}
+
+void SkActive::init() 
+{
+    fAnimators = fApply.fAnimators;
+    int animators = fAnimators.count();
+    fInterpolators.setCount(animators);
+    memset(fInterpolators.begin(), 0, animators * sizeof(SkOperandInterpolator*));
+    fState.setCount(animators);
+    int index;
+    for (index = 0; index < animators; index++)
+        fInterpolators[index] = SkNEW(SkOperandInterpolator);
+    initState(&fApply, 0);
+//  for (index = 0; index < animators; index++)
+//      fState[index].bumpSave();
+    SkASSERT(fInterpolators.count() == fAnimators.count());
+}
+
+SkActive::~SkActive() {
+    int index;
+    for (index = 0; index < fSaveRestore.count(); index++)
+        delete[] fSaveRestore[index];
+    for (index = 0; index < fSaveInterpolators.count(); index++)
+        delete[] fSaveInterpolators[index];
+    for (index = 0; index < fInterpolators.count(); index++)
+        delete fInterpolators[index];
+}
+
+void SkActive::advance() {
+    if (fDrawMax < fDrawIndex)
+        fDrawMax = fDrawIndex;
+    fDrawIndex += fAnimators.count();
+}
+
+void SkActive::append(SkApply* apply) {
+    int oldCount = fAnimators.count();
+    SkTDAnimateArray& animates = apply->fAnimators;
+    int newCount = animates.count();
+    int index;
+    int total = oldCount + newCount;
+    if (total == 0)
+        return;
+    fInterpolators.setCount(total);
+    memset(&fInterpolators.begin()[oldCount], 0, newCount * sizeof(SkOperandInterpolator*));
+    for (index = oldCount; index < total; index++)
+        fInterpolators[index] = SkNEW(SkOperandInterpolator);
+    fAnimators.setCount(total);
+    memcpy(&fAnimators[oldCount], animates.begin(), sizeof(fAnimators[0]) *
+        newCount);
+    fState.setCount(total);
+    initState(apply, oldCount);
+    SkASSERT(fApply.scope == apply->scope);
+    for (index = 0; index < newCount; index++) {
+        SkAnimateBase* test = animates[index];
+//      SkASSERT(fApply.scope == test->fTarget || fApply.scope->contains(test->fTarget));
+        SkActive::SkState& testState = fState[oldCount + index];
+        for (int inner = 0; inner < oldCount; inner++) {
+            SkAnimateBase* oldGuard = fAnimators[inner];
+            SkActive::SkState& oldState = fState[inner];
+            if (oldGuard->fTarget == test->fTarget && oldGuard->fFieldInfo == test->fFieldInfo &&
+                    testState.fBegin == oldState.fBegin) {
+                delete fInterpolators[inner];
+                fInterpolators.remove(inner);
+                fAnimators.remove(inner);
+                testState.fSave = oldState.fSave;
+                if (oldState.fUnpostedEndEvent) {
+//                  SkDEBUGF(("%8x %8x active append: post on end\n", this, oldGuard));
+                    fMaker.postOnEnd(oldGuard, oldState.fBegin + oldState.fDuration);
+                }
+                fState.remove(inner);
+                if (fApply.restore) {
+                    int saveIndex = fSaveRestore.count();
+                    SkASSERT(fSaveInterpolators.count() == saveIndex);
+                    saveIndex += inner;
+                    do {
+                        saveIndex -= oldCount;
+                        delete[] fSaveRestore[saveIndex];
+                        fSaveRestore.remove(saveIndex);
+                        delete[] fSaveInterpolators[saveIndex]; 
+                        fSaveInterpolators.remove(saveIndex);
+                    } while (saveIndex > 0);
+                }
+                oldCount--;
+                break;
+            }
+        }
+    }
+//  total = oldCount + newCount;
+//  for (index = oldCount; index < total; index++)
+//      fState[index].bumpSave();
+    SkASSERT(fInterpolators.count() == fAnimators.count());
+}
+
+void SkActive::appendSave(int oldCount) {
+    SkASSERT(fDrawMax == 0);    // if true, we can optimize below quite a bit
+    int newCount = fAnimators.count();
+    int saveIndex = fSaveRestore.count();
+    SkASSERT(fSaveInterpolators.count() == saveIndex);
+    int records = saveIndex / oldCount;
+    int newTotal = records * newCount;
+    fSaveRestore.setCount(newTotal);
+    do {
+        saveIndex -= oldCount;
+        newTotal -= newCount;
+        SkASSERT(saveIndex >= 0);
+        SkASSERT(newTotal >= 0);
+        memmove(&fSaveRestore[newTotal], &fSaveRestore[saveIndex], oldCount);
+        memset(&fSaveRestore[newTotal + oldCount], 0, 
+            sizeof(fSaveRestore[0]) * (newCount - oldCount));
+        memmove(&fSaveInterpolators[newTotal], 
+            &fSaveInterpolators[saveIndex], oldCount);
+        memset(&fSaveInterpolators[newTotal + oldCount], 0, 
+            sizeof(fSaveRestore[0]) * (newCount - oldCount));
+    } while (saveIndex > 0);
+    SkASSERT(newTotal == 0);
+}
+
+void SkActive::calcDurations(int index) 
+{
+    SkAnimateBase* animate = fAnimators[index];
+    SkMSec duration = animate->dur;
+    SkState& state = fState[index];
+    switch (state.fMode) {
+      case SkApply::kMode_immediate:
+      case SkApply::kMode_create:
+        duration = state.fSteps ? state.fSteps * SK_MSec1 : 1;
+        break;
+//    case SkApply::kMode_hold: {
+//      int entries = animate->entries();
+//      SkScriptValue value;
+//      value.fOperand = animate->getValues()[entries - 1];
+//      value.fType = animate->getValuesType();
+//      bool result = SkScriptEngine::ConvertTo(NULL, SkType_Int, &value);
+//      SkASSERT(result);
+//      duration = value.fOperand.fS32 * SK_MSec1;
+//      break;
+//    }
+    }
+    state.fDuration = duration;
+    SkMSec maxTime = state.fBegin + duration;
+    if (fMaxTime < maxTime)
+        fMaxTime = maxTime;
+}
+
+void SkActive::create(SkDrawable* drawable, SkMSec time) {
+    fApply.fLastTime = time;
+    fApply.refresh(fMaker);
+    for (int index = 0; index < fAnimators.count(); index++) {
+        SkAnimateBase* animate = fAnimators[index];
+        SkOperandInterpolator& interpolator = *fInterpolators[index];
+        int count = animate->components();
+        if (animate->formula.size() > 0) {
+            SkTDOperandArray values;
+            values.setCount(count);
+            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);
+        } else {
+            SkAutoSTMalloc<16, SkOperand> values(count);
+            interpolator.timeToValues(time, values.get());
+            fApply.applyValues(index, values.get(), count, animate->getValuesType(), time);
+        }
+    }
+    drawable->enable(fMaker);
+    SkASSERT(fAnimators.count() == fInterpolators.count());
+}
+
+bool SkActive::immediate(bool enable) {
+    SkMSec time = 0;
+    bool result = false;
+    SkDrawable* drawable = fApply.scope;
+    SkMSec final = fMaxTime;
+    do {
+        bool applied = fAnimators.count() == 0;
+        fApply.fLastTime = time;
+        fApply.refresh(fMaker);
+        for (int index = 0; index < fAnimators.count(); index++) {
+            SkAnimateBase* animate = fAnimators[index];
+            SkState& state = fState[index];
+            if (state.fMode != SkApply::kMode_immediate)
+                continue;
+            if (state.fBegin > time)
+                continue;
+            if (time > state.fBegin + state.fDuration)
+                continue;
+            applied = true;
+            SkOperandInterpolator& interpolator = *fInterpolators[index];
+            int count = animate->components();
+            if (animate->formula.size() > 0) {
+                SkTDOperandArray values;
+                values.setCount(count);
+                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);
+            } else {
+                SkAutoSTMalloc<16, SkOperand> values(count);
+                interpolator.timeToValues(time, values.get());
+                fApply.applyValues(index, values.get(), count, animate->getValuesType(), time);
+            }
+        }
+        if (enable)
+            drawable->enable(fMaker);
+        else if (applied)
+            result |= drawable->draw(fMaker);
+        time += SK_MSec1;
+    } while (time <= final);
+    return result;
+}
+
+void SkActive::fixInterpolator(SkBool save) {
+    int animators = fAnimators.count();
+    for (int index = 0; index < animators; index++) {
+        SkAnimateBase* animate = fAnimators[index];
+        if (save) { // saved slots increased
+            animate->refresh(fMaker);
+            SkOperand* values = animate->getValues();
+            setInterpolator(index, values);
+            saveInterpolatorValues(index);
+        } else
+            restoreInterpolatorValues(index);
+    }
+}
+
+SkMSec SkActive::getTime(SkMSec inTime, int animatorIndex) {
+    fState[animatorIndex].fTicks = inTime;
+    return inTime - fState[animatorIndex].fStartTime;
+}
+
+bool SkActive::initializeSave() {
+    int animators = fAnimators.count();
+    int activeTotal = fDrawIndex + animators;
+    int oldCount = fSaveRestore.count();
+    if (oldCount < activeTotal) {
+        fSaveRestore.setCount(activeTotal);
+        memset(&fSaveRestore[oldCount], 0, sizeof(fSaveRestore[0]) * (activeTotal - oldCount));
+        SkASSERT(fSaveInterpolators.count() == oldCount);
+        fSaveInterpolators.setCount(activeTotal);
+        memset(&fSaveInterpolators[oldCount], 0, 
+            sizeof(fSaveInterpolators[0]) * (activeTotal - oldCount));
+        return true;
+    }
+    return false;
+}
+
+void SkActive::initState(SkApply* apply, int offset) {
+    int count = fState.count();
+    for (int index = offset; index < count; index++) {
+        SkState& state = fState[index];
+        SkAnimateBase* animate = fAnimators[index];
+#if 0 // def SK_DEBUG
+        if (animate->fHasEndEvent)
+            SkDebugf("%8x %8x active initState:\n", this, animate);
+#endif
+        SkOperand* from = animate->getValues();
+        state.fStartTime = state.fBegin = apply->begin + animate->begin;
+        state.fMode = apply->mode;
+        state.fTransition = apply->transition;
+#if 0
+        state.fPickup = (SkBool8) apply->pickup;
+#endif
+        state.fRestore = (SkBool8) apply->restore;
+        state.fSave = apply->begin;
+        state.fStarted = false;
+        state.fSteps = apply->steps;
+        state.fTicks = 0;
+        state.fUnpostedEndEvent = (SkBool8) animate->fHasEndEvent; 
+        calcDurations(index);
+        setInterpolator(index, from);
+    }
+    if (count == 0 && (apply->mode == SkApply::kMode_immediate || apply->mode == SkApply::kMode_create))
+        fMaxTime = apply->begin + apply->steps * SK_MSec1;
+}
+
+void SkActive::pickUp(SkActive* existing) {
+    SkTDOperandArray existingValues;
+    for (int index = 0; index < fAnimators.count(); index++) {
+        SkAnimateBase* animate = fAnimators[index];
+        SkASSERT(animate->getValuesType() == SkType_Float);
+        int components = animate->components();
+        SkOperand* from = animate->getValues();
+        SkOperand* to = &from[animate->components()];
+        existingValues.setCount(components);
+        existing->fInterpolators[index]->timeToValues(
+            existing->fState[index].fTicks - existing->fState[index].fStartTime, existingValues.begin());
+        SkScalar originalSum = 0;
+        SkScalar workingSum = 0;
+        for (int cIndex = 0; cIndex < components; cIndex++) {
+            SkScalar delta = to[cIndex].fScalar - from[cIndex].fScalar;
+            originalSum += SkScalarMul(delta, delta);
+            delta = to[cIndex].fScalar - existingValues[cIndex].fScalar;
+            workingSum += SkScalarMul(delta, delta);
+        }
+        if (workingSum < originalSum) {
+            SkScalar originalDistance = SkScalarSqrt(originalSum);
+            SkScalar workingDistance = SkScalarSqrt(workingSum);
+            existing->fState[index].fDuration = (SkMSec) SkScalarMulDiv(fState[index].fDuration, 
+                workingDistance, originalDistance);
+        }
+        fInterpolators[index]->reset(components, 2, SkType_Float);
+        fInterpolators[index]->setKeyFrame(0, 0, existingValues.begin(), animate->blend[0]);
+        fInterpolators[index]->setKeyFrame(1, fState[index].fDuration, to, animate->blend[0]);
+    }
+}
+
+void SkActive::resetInterpolators() {
+    int animators = fAnimators.count();
+    for (int index = 0; index < animators; index++) {
+        SkAnimateBase* animate = fAnimators[index];
+        SkOperand* values = animate->getValues();
+        setInterpolator(index, values);
+    }
+}
+
+void SkActive::resetState() {
+    fDrawIndex = 0;
+    int count = fState.count();
+    for (int index = 0; index < count; index++) {
+        SkState& state = fState[index];
+        SkAnimateBase* animate = fAnimators[index];
+#if 0 // def SK_DEBUG
+        if (animate->fHasEndEvent)
+            SkDebugf("%8x %8x active resetState: has end event\n", this, animate);
+#endif
+        state.fStartTime = state.fBegin = fApply.begin + animate->begin;
+        state.fStarted = false;
+        state.fTicks = 0;
+    }
+}
+
+void SkActive::restoreInterpolatorValues(int index) {
+    SkOperandInterpolator& interpolator = *fInterpolators[index];
+    index += fDrawIndex ;
+    int count = interpolator.getValuesCount();
+    memcpy(interpolator.getValues(), fSaveInterpolators[index], count * sizeof(SkOperand));
+}
+
+void SkActive::saveInterpolatorValues(int index) {
+    SkOperandInterpolator& interpolator = *fInterpolators[index];
+    index += fDrawIndex ;
+    int count = interpolator.getValuesCount();
+    SkOperand* cache = new SkOperand[count];    // this should use sk_malloc/sk_free since SkOperand does not have a constructor/destructor
+    fSaveInterpolators[index] = cache;
+    memcpy(cache,   interpolator.getValues(), count * sizeof(SkOperand));
+}
+
+void SkActive::setInterpolator(int index, SkOperand* from) {
+    if (from == NULL) // legitimate for set string
+        return;
+    SkAnimateBase* animate = fAnimators[index];
+    int entries = animate->entries();
+    SkASSERT(entries > 0);
+    SkMSec duration = fState[index].fDuration;
+    int components = animate->components();
+    SkOperandInterpolator& interpolator = *fInterpolators[index];
+    interpolator.reset(components, entries == 1 ? 2 : entries, animate->getValuesType()); 
+    interpolator.setMirror(SkToBool(animate->fMirror));
+    interpolator.setReset(SkToBool(animate->fReset));
+    interpolator.setRepeatCount(animate->repeat);
+    if (entries == 1) {
+        interpolator.setKeyFrame(0, 0, from, animate->blend[0]);
+        interpolator.setKeyFrame(1, duration, from, animate->blend[0]);
+        return;
+    }
+    for (int entry = 0; entry < entries; entry++) {
+        int blendIndex = SkMin32(animate->blend.count() - 1, entry);
+        interpolator.setKeyFrame(entry, entry * duration / (entries - 1), from, 
+            animate->blend[blendIndex]);
+        from += components;
+    }
+}
+
+void SkActive::setSteps(int steps) {
+    int count = fState.count();
+    fMaxTime = 0;
+    for (int index = 0; index < count; index++) {
+        SkState& state = fState[index];
+        state.fSteps = steps;
+        calcDurations(index);
+    }
+}
+
+void SkActive::start() {
+    int count = fState.count();
+    SkASSERT(count == fAnimators.count());
+    SkASSERT(count == fInterpolators.count());
+    for (int index = 0; index < count; index++) {
+        SkState& state = fState[index];
+        if (state.fStarted)
+            continue;
+        state.fStarted = true;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+        SkString debugOut;
+        SkMSec time = fMaker.getAppTime();
+        debugOut.appendS32(time - fMaker.fDebugTimeBase);
+        debugOut.append(" active start adjust delay id=");
+        debugOut.append(fApply._id);
+        debugOut.append("; ");
+        debugOut.append(fAnimators[index]->_id);
+        debugOut.append("=");
+        debugOut.appendS32(fAnimators[index]->fStart - fMaker.fDebugTimeBase);
+        debugOut.append(":");
+        debugOut.appendS32(state.fStartTime);
+#endif
+        if (state.fStartTime > 0) {
+            SkMSec future = fAnimators[index]->fStart + state.fStartTime;
+            if (future > fMaker.fEnableTime)
+                fMaker.notifyInvalTime(future);
+            else
+                fMaker.notifyInval();
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            debugOut.append(":");
+            debugOut.appendS32(future - fMaker.fDebugTimeBase);
+#endif
+        }
+        if (state.fStartTime >= fMaker.fAdjustedStart) {
+            state.fStartTime -= fMaker.fAdjustedStart;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            debugOut.append(" (less adjust = ");
+            debugOut.appendS32(fMaker.fAdjustedStart);
+#endif
+        }
+        state.fStartTime += fAnimators[index]->fStart;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+        debugOut.append(") new start = ");
+        debugOut.appendS32(state.fStartTime - fMaker.fDebugTimeBase);
+        SkDebugf("%s\n", debugOut.c_str());
+//      SkASSERT((int) (state.fStartTime - fMaker.fDebugTimeBase) >= 0);
+#endif
+    }
+    SkASSERT(fAnimators.count() == fInterpolators.count());
+}
+
+#ifdef SK_DEBUG
+void SkActive::validate() {
+    int count = fState.count();
+    SkASSERT(count == fAnimators.count());
+    SkASSERT(count == fInterpolators.count());
+    for (int index = 0; index < count; index++) {
+        SkASSERT(fAnimators[index]);
+        SkASSERT(fInterpolators[index]);
+//      SkAnimateBase* test = fAnimators[index];
+//      SkASSERT(fApply.scope == test->fTarget || fApply.scope->contains(test->fTarget));
+    }
+}
+#endif
+
+// think about this
+// there should only be one animate object, not two, to go up and down
+// when the apply with reverse came into play, it needs to pick up the value
+// of the existing animate object then remove it from the list
+// 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 
+// value for the new one somehow.
+
+//void SkActive::SkState::bumpSave() {
+//  if (fMode != SkApply::kMode_hold) 
+//      return;
+//  if (fTransition == SkApply::kTransition_reverse) {
+//      if (fSave > 0)
+//          fSave -= SK_MSec1;
+//  } else if (fSave < fDuration)
+//      fSave += SK_MSec1;
+//}
+
+SkMSec SkActive::SkState::getRelativeTime(SkMSec time) {
+    SkMSec result = time;
+//  if (fMode == SkApply::kMode_hold)
+//      result = fSave;
+//  else
+    if (fTransition == SkApply::kTransition_reverse) {
+        if (SkMSec_LT(fDuration, time))
+            result = 0;
+        else
+            result = fDuration - time;
+    }
+    return result;
+}
+
+
diff --git a/legacy/src/animator/SkAnimateActive.h b/legacy/src/animator/SkAnimateActive.h
new file mode 100644
index 0000000..33d0164
--- /dev/null
+++ b/legacy/src/animator/SkAnimateActive.h
@@ -0,0 +1,79 @@
+
+/*
+ * 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 SkAnimateActive_DEFINED
+#define SkAnimateActive_DEFINED
+
+#include "SkDisplayApply.h"
+#include "SkOperandInterpolator.h"
+#include "SkIntArray.h"
+
+class SkAnimateMaker;
+
+class SkActive {
+public:
+    SkActive(SkApply& , SkAnimateMaker& );
+    ~SkActive();
+    void advance();
+    void append(SkApply* );
+    void calcDurations(int index);
+    void create(SkDrawable* scope, SkMSec time);
+    bool draw() { return immediate(false); }
+    bool enable() { return immediate(true); }
+    void init( );
+    SkMSec getTime(SkMSec inTime, int animatorIndex);
+    void pickUp(SkActive* existing);
+    void reset() { fDrawIndex = 0; }
+    void setInterpolator(int index, SkOperand* from);
+    void start();
+#ifdef SK_DEBUG
+    void validate();
+#endif
+private:
+    void appendSave(int oldCount);
+    void fixInterpolator(SkBool save);
+    bool immediate(bool enable);
+    bool initializeSave();
+    void initState(SkApply* , int offset);
+    void resetInterpolators();
+    void resetState();
+    void restoreInterpolatorValues(int index);
+    void saveInterpolatorValues(int index);
+    void setSteps(int steps);
+    struct SkState {
+//      void bumpSave();
+        SkMSec getRelativeTime(SkMSec time);
+        SkApply::Mode fMode;
+        SkApply::Transition fTransition;
+        SkBool8 fPickup;
+        SkBool8 fRestore;
+        SkBool8 fStarted;
+        SkBool8 fUnpostedEndEvent;
+        int32_t fSteps;
+        SkMSec fBegin;
+        SkMSec fStartTime;
+        SkMSec fDuration;
+        SkMSec fSave;
+        SkMSec fTicks;
+    };
+    SkActive& operator= (const SkActive& );
+    SkTDArray<SkOperandInterpolator*> fInterpolators;
+    SkApply& fApply;
+    SkTDArray<SkState> fState;  // one per animator
+    SkTDOperandPtrArray fSaveRestore;   // if apply has restore="true"
+    SkTDOperandPtrArray fSaveInterpolators;
+    SkTDAnimateArray fAnimators;
+    SkMSec fMaxTime;    // greatest of all animation durations; only used by immediate mode
+    SkAnimateMaker& fMaker;
+    int fDrawIndex;
+    int fDrawMax;
+    friend class SkApply;
+};
+
+#endif // SkAnimateActive_DEFINED
diff --git a/legacy/src/animator/SkAnimateBase.cpp b/legacy/src/animator/SkAnimateBase.cpp
new file mode 100644
index 0000000..2c9e862
--- /dev/null
+++ b/legacy/src/animator/SkAnimateBase.cpp
@@ -0,0 +1,239 @@
+
+/*
+ * 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 "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateProperties.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayApply.h"
+#include "SkDrawable.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAnimateBase::fInfo[] = {
+    SK_MEMBER(begin, MSec),
+    SK_MEMBER_ARRAY(blend, Float),
+    SK_MEMBER(dur, MSec),
+    SK_MEMBER_PROPERTY(dynamic, Boolean),
+    SK_MEMBER(field, String),   // name of member info in target
+    SK_MEMBER(formula, DynamicString),
+    SK_MEMBER(from, DynamicString),
+    SK_MEMBER(lval, DynamicString),
+    SK_MEMBER_PROPERTY(mirror, Boolean),
+    SK_MEMBER(repeat, Float),
+    SK_MEMBER_PROPERTY(reset, Boolean),
+    SK_MEMBER_PROPERTY(step, Int),
+    SK_MEMBER(target, DynamicString),
+    SK_MEMBER(to, DynamicString),
+    SK_MEMBER_PROPERTY(values, DynamicString)
+};
+
+#endif
+
+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), 
+        fMirror(0), fReset(0), fResetPending(0), fTargetIsScope(0) {
+    blend.setCount(1);
+    blend[0] = SK_Scalar1;
+}
+
+SkAnimateBase::~SkAnimateBase() {
+    SkDisplayTypes type = fValues.getType();
+    if (type == SkType_String || type == SkType_DynamicString) {
+        SkASSERT(fValues.count() == 1);
+        delete fValues[0].fString;
+    }
+}
+
+int SkAnimateBase::components() { 
+    return 1; 
+}
+
+SkDisplayable* SkAnimateBase::deepCopy(SkAnimateMaker* maker) {
+    SkAnimateBase* result = (SkAnimateBase*) INHERITED::deepCopy(maker);
+    result->fApply = fApply;
+    result->fFieldInfo =fFieldInfo;
+    result->fHasValues = false;
+    return result;
+}
+
+void SkAnimateBase::dirty() {
+    fChanged = true;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkAnimateBase::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (target.size() > 0)
+        SkDebugf("target=\"%s\" ", target.c_str());
+    else if (fTarget && strcmp(fTarget->id, ""))
+        SkDebugf("target=\"%s\" ", fTarget->id);
+    if (lval.size() > 0)
+        SkDebugf("lval=\"%s\" ", lval.c_str());
+    if (field.size() > 0)
+        SkDebugf("field=\"%s\" ", field.c_str());
+    else if (fFieldInfo)
+        SkDebugf("field=\"%s\" ", fFieldInfo->fName);
+    if (formula.size() > 0)
+        SkDebugf("formula=\"%s\" ", formula.c_str());
+    else {
+        if (from.size() > 0)
+            SkDebugf("from=\"%s\" ", from.c_str());
+        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
+
+SkDisplayable* SkAnimateBase::getParent() const {
+    return (SkDisplayable*) fApply;
+}
+
+bool SkAnimateBase::getProperty(int index, SkScriptValue* value) const {
+    int boolResult;
+    switch (index) {
+        case SK_PROPERTY(dynamic):
+            boolResult = fDynamic;
+            goto returnBool;
+        case SK_PROPERTY(mirror):
+            boolResult = fMirror;
+            goto returnBool;
+        case SK_PROPERTY(reset):
+            boolResult = fReset;
+returnBool:
+            value->fOperand.fS32 = SkToBool(boolResult);
+            value->fType = SkType_Boolean;
+            break;
+        case SK_PROPERTY(step):
+            if (fApply == NULL)
+                return false;    // !!! notify there's an error?
+            fApply->getStep(value);
+            break;
+        case SK_PROPERTY(values):
+            value->fOperand.fString = (SkString*) &to;
+            value->fType = SkType_String;
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+bool SkAnimateBase::hasExecute() const 
+{
+    return false; 
+}
+
+void SkAnimateBase::onEndElement(SkAnimateMaker& maker) {
+    fChanged = false;
+    setTarget(maker);
+    if (field.size()) {
+        SkASSERT(fTarget);
+        fFieldInfo = fTarget->getMember(field.c_str());
+        field.reset();
+    }
+    if (lval.size()) {
+        // lval must be of the form x[y]
+        const char* lvalStr = lval.c_str();
+        const char* arrayEnd = strchr(lvalStr, '[');
+        if (arrayEnd == NULL)
+            return; //should this return an error?
+        size_t arrayNameLen = arrayEnd - lvalStr;
+        SkString arrayStr(lvalStr, arrayNameLen);
+        SkASSERT(fTarget);  //this return an error?
+        fFieldInfo = fTarget->getMember(arrayStr.c_str());
+        SkString scriptStr(arrayEnd + 1, lval.size() - arrayNameLen - 2);
+        SkAnimatorScript::EvaluateInt(maker, this, scriptStr.c_str(), &fFieldOffset);
+    }
+}
+
+void SkAnimateBase::packARGB(SkScalar array[], int count, SkTDOperandArray* converted) 
+{ 
+    SkASSERT(count == 4);
+    converted->setCount(1);
+    SkColor color = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), 
+        SkScalarRound(array[2]), SkScalarRound(array[3]));
+    (*converted)[0].fS32 = color;
+}
+
+
+
+void SkAnimateBase::refresh(SkAnimateMaker& ) {
+}
+
+bool SkAnimateBase::setParent(SkDisplayable* apply) {
+    SkASSERT(apply->isApply());
+    fApply = (SkApply*) apply;
+    return false;
+}
+
+bool SkAnimateBase::setProperty(int index, SkScriptValue& value) {
+    bool boolValue = SkToBool(value.fOperand.fS32);
+    switch (index) {
+        case SK_PROPERTY(dynamic):
+            fDynamic = boolValue;
+            goto checkForBool;
+        case SK_PROPERTY(values):
+            fHasValues = true;
+            SkASSERT(value.fType == SkType_String);
+            to = *value.fOperand.fString;
+            break;
+        case SK_PROPERTY(mirror):
+            fMirror = boolValue;
+            goto checkForBool;
+        case SK_PROPERTY(reset):
+            fReset = boolValue;
+checkForBool:
+            SkASSERT(value.fType == SkType_Boolean);
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+void SkAnimateBase::setTarget(SkAnimateMaker& maker) {
+    if (target.size()) {
+        SkAnimatorScript engine(maker, this, SkType_Displayable);
+        const char* script = target.c_str();
+        SkScriptValue scriptValue;
+        bool success = engine.evaluateScript(&script, &scriptValue);
+        if (success && scriptValue.fType == SkType_Displayable)
+            fTarget = scriptValue.fOperand.fDrawable;
+        else if (maker.find(target.c_str(), (SkDisplayable**) &fTarget) == false) {
+            if (fApply->getMode() == SkApply::kMode_create)
+                return; // may not be an error
+            if (engine.getError() != SkScriptEngine::kNoError)
+                maker.setScriptError(engine);
+            else {
+                maker.setErrorNoun(target);
+                maker.setErrorCode(SkDisplayXMLParserError::kTargetIDNotFound);
+            }
+            return;
+        }
+        if (fApply && fApply->getMode() != SkApply::kMode_create)
+            target.reset();
+    }
+}
+
+bool SkAnimateBase::targetNeedsInitialization() const { 
+    return false; 
+}
+
+
diff --git a/legacy/src/animator/SkAnimateBase.h b/legacy/src/animator/SkAnimateBase.h
new file mode 100644
index 0000000..df8d38a
--- /dev/null
+++ b/legacy/src/animator/SkAnimateBase.h
@@ -0,0 +1,83 @@
+
+/*
+ * 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 SkAnimateBase_DEFINED
+#define SkAnimateBase_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMath.h"
+#include "SkMemberInfo.h"
+#include "SkTypedArray.h"
+
+class SkApply;
+class SkDrawable;
+
+class SkAnimateBase : public SkDisplayable {
+public:
+    DECLARE_MEMBER_INFO(AnimateBase);
+    SkAnimateBase();
+    virtual ~SkAnimateBase();
+    virtual int components();
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual void dirty();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    int entries() { return fValues.count() / components(); }
+    virtual bool hasExecute() const;
+    bool isDynamic() const { return SkToBool(fDynamic); }
+    virtual SkDisplayable* getParent() const;
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    SkMSec getStart() const { return fStart; }
+    SkOperand* getValues() { return fValues.begin(); }
+    SkDisplayTypes getValuesType() { return fValues.getType(); }
+    virtual void onEndElement(SkAnimateMaker& );
+    void packARGB(SkScalar [], int count, SkTDOperandArray* );
+    virtual void refresh(SkAnimateMaker& );
+    void setChanged(bool changed) { fChanged = changed; }
+    void setHasEndEvent() { fHasEndEvent = true; }
+    virtual bool setParent(SkDisplayable* );
+    virtual bool setProperty(int index, SkScriptValue& value);
+    void setTarget(SkAnimateMaker& );
+    virtual bool targetNeedsInitialization() const;
+protected:
+    SkMSec begin;
+    SkTDScalarArray blend;
+    SkMSec dur;
+    // !!! make field part of a union with fFieldInfo, or fValues, something known later?
+    SkString field; // temporary; once target is known, this is reset
+    SkString formula;
+    SkString from;
+    SkString lval;
+    SkScalar repeat;
+    SkString target;    // temporary; once target is known, this is reset
+    SkString to;
+    SkApply* fApply;
+    const SkMemberInfo* fFieldInfo;
+    int fFieldOffset;
+    SkMSec fStart;  // corrected time when this apply was enabled
+    SkDrawable* fTarget;
+    SkTypedArray fValues;
+    unsigned fChanged : 1; // true when value referenced by script has changed
+    unsigned fDelayed : 1;  // enabled, but undrawn pending delay
+    unsigned fDynamic : 1;
+    unsigned fHasEndEvent : 1;
+    unsigned fHasValues : 1;        // set if 'values' passed instead of 'to'
+    unsigned fMirror : 1;
+    unsigned fReset : 1;
+    unsigned fResetPending : 1;
+    unsigned fTargetIsScope : 1;
+private:
+    typedef SkDisplayable INHERITED;
+    friend class SkActive;
+    friend class SkApply;
+    friend class SkDisplayList;
+};
+
+#endif // SkAnimateBase_DEFINED
diff --git a/legacy/src/animator/SkAnimateField.cpp b/legacy/src/animator/SkAnimateField.cpp
new file mode 100644
index 0000000..ac96bbc
--- /dev/null
+++ b/legacy/src/animator/SkAnimateField.cpp
@@ -0,0 +1,122 @@
+
+/*
+ * 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 "SkAnimate.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawable.h"
+#include "SkParse.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAnimate::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAnimate);
+
+SkAnimate::SkAnimate() : fComponents(0) {
+}
+
+SkAnimate::~SkAnimate() {
+}
+
+int SkAnimate::components() { 
+    return fComponents; 
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkAnimate::dump(SkAnimateMaker* maker) {
+    INHERITED::dump(maker); //from animateBase
+    //SkSet inherits from this class
+    if (getType() != SkType_Set) {
+        if (fMirror)
+            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) 
+                    SkDebugf(",");
+                firstElem = false;
+#ifdef SK_CAN_USE_FLOAT
+                SkDebugf("%g", SkScalarToFloat(blend[i]));
+#else
+                SkDebugf("%x", blend[i]);
+#endif
+            }
+            SkDebugf("]\" ");
+        }
+        SkDebugf("/>\n");//i assume that if it IS, we will do it separately
+    }
+}
+#endif
+
+bool SkAnimate::resolveCommon(SkAnimateMaker& maker) {
+    if (fTarget == NULL) // if NULL, recall onEndElement after apply closes and sets target to scope
+        return false;
+    INHERITED::onEndElement(maker);
+    return maker.hasError() == false;
+}
+
+void SkAnimate::onEndElement(SkAnimateMaker& maker) {
+    bool resolved = resolveCommon(maker);
+    if (resolved && fFieldInfo == NULL) {
+        maker.setErrorNoun(field);
+        maker.setErrorCode(SkDisplayXMLParserError::kFieldNotInTarget);
+    }
+    if (resolved == false || fFieldInfo == NULL)
+        return;
+    SkDisplayTypes outType = fFieldInfo->getType();
+    if (fHasValues) {
+        SkASSERT(to.size() > 0);
+        fFieldInfo->setValue(maker, &fValues, 0, 0, NULL, outType, to);
+        SkASSERT(0);
+        // !!! this needs to set fComponents 
+        return;
+    }
+    fComponents = fFieldInfo->getCount();
+    if (fFieldInfo->fType == SkType_Array) {
+        SkTypedArray* array = (SkTypedArray*) fFieldInfo->memberData(fTarget);
+        int count = array->count();
+        if (count > 0)
+            fComponents = count;
+    }
+    if (outType == SkType_ARGB) {
+        fComponents <<= 2;  // four color components
+        outType = SkType_Float;
+    }
+    fValues.setType(outType);
+    if (formula.size() > 0){
+        fComponents = 1;
+        from.set("0");
+        to.set("dur");
+        outType = SkType_MSec;
+    }
+    int max = fComponents * 2;
+    fValues.setCount(max);
+    memset(fValues.begin(), 0, max * sizeof(fValues.begin()[0]));
+    fFieldInfo->setValue(maker, &fValues, fFieldOffset, max, this, outType, from);
+    fFieldInfo->setValue(maker, &fValues, fComponents + fFieldOffset, max, this, outType, to);
+}
+
diff --git a/legacy/src/animator/SkAnimateMaker.cpp b/legacy/src/animator/SkAnimateMaker.cpp
new file mode 100644
index 0000000..414e728
--- /dev/null
+++ b/legacy/src/animator/SkAnimateMaker.cpp
@@ -0,0 +1,370 @@
+
+/*
+ * 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 "SkAnimateMaker.h"
+#include "SkAnimator.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayable.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayList.h"
+#include "SkDisplayMovie.h"
+#include "SkDisplayType.h"
+#include "SkExtras.h"
+#include "SkMemberInfo.h"
+#include "SkStream.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+class DefaultTimeline : public SkAnimator::Timeline {
+    virtual SkMSec getMSecs() const {
+        return SkTime::GetMSecs();
+    }
+} gDefaultTimeline;
+
+SkAnimateMaker::SkAnimateMaker(SkAnimator* animator, SkCanvas* canvas, SkPaint* paint)
+    : 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)
+{
+    fScreenplay.time = 0;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    fDebugTimeBase = (SkMSec) -1;
+#endif
+#ifdef SK_DUMP_ENABLED
+    fDumpEvents = fDumpGConditions = fDumpPosts = false;
+#endif
+}
+
+SkAnimateMaker::~SkAnimateMaker() {
+    deleteMembers();
+}
+
+#if 0
+SkMSec SkAnimateMaker::adjustDelay(SkMSec expectedBase, SkMSec delay) {
+    SkMSec appTime = (*fTimeCallBack)();
+    if (appTime)
+        delay -= appTime - expectedBase;
+    if (delay < 0)
+        delay = 0;
+    return delay;
+}
+#endif
+
+void SkAnimateMaker::appendActive(SkActive* active) {
+    fDisplayList.append(active);
+}
+
+void SkAnimateMaker::clearExtraPropertyCallBack(SkDisplayTypes type) {
+    SkExtras** end = fExtras.end();
+    for (SkExtras** extraPtr = fExtras.begin(); extraPtr < end; extraPtr++) {
+        SkExtras* extra = *extraPtr;
+        if (extra->definesType(type)) {
+            extra->fExtraCallBack = NULL;
+            extra->fExtraStorage = NULL;
+            break;
+        }
+    }
+}
+
+bool SkAnimateMaker::computeID(SkDisplayable* displayable, SkDisplayable* parent, SkString* newID) {
+    const char* script;
+  if (findKey(displayable, &script) == false)
+        return true;
+    return SkAnimatorScript::EvaluateString(*this, displayable, parent, script, newID);
+}
+
+SkDisplayable* SkAnimateMaker::createInstance(const char name[], size_t len) {
+    SkDisplayTypes type = SkDisplayType::GetType(this, name, len );
+    if ((int)type >= 0) 
+        return SkDisplayType::CreateInstance(this, type);
+    return NULL;
+}
+
+// differs from SkAnimator::decodeStream in that it does not reset error state
+bool SkAnimateMaker::decodeStream(SkStream* stream)
+{
+    SkDisplayXMLParser parser(*this);
+    return parser.parse(*stream);
+}
+
+// differs from SkAnimator::decodeURI in that it does not set URI base
+bool SkAnimateMaker::decodeURI(const char uri[]) {
+//  SkDebugf("animator decode %s\n", uri);
+
+//    SkStream* stream = SkStream::GetURIStream(fPrefix.c_str(), uri);
+    SkStream* stream = new SkFILEStream(uri);
+
+    SkAutoTDelete<SkStream> autoDel(stream);
+    bool success = decodeStream(stream);
+    if (hasError() && fError.hasNoun() == false)
+        fError.setNoun(uri);
+    return success;
+}
+
+#if defined SK_DEBUG && 0
+//used for the if'd out section of deleteMembers
+#include "SkTSearch.h"
+
+extern "C" {
+    int compare_disp(const void* a, const void* b) {
+        return *(const SkDisplayable**)a - *(const SkDisplayable**)b;
+    }
+}
+#endif
+
+void SkAnimateMaker::delayEnable(SkApply* apply, SkMSec time) {
+    int index = fDelayed.find(apply);
+    if (index < 0) {
+        *fDelayed.append() = apply;
+    }
+    
+    (new SkEvent(SK_EventType_Delay, fAnimator->getSinkID()))->postTime(time);
+}
+
+void SkAnimateMaker::deleteMembers() {
+    int index;
+#if defined SK_DEBUG && 0
+    //this code checks to see if helpers are among the children, but it is not complete -
+    //it should check the children of the children
+    int result;
+    SkTDArray<SkDisplayable*> children(fChildren.begin(), fChildren.count());
+    SkQSort(children.begin(), children.count(), sizeof(SkDisplayable*),compare_disp);
+    for (index = 0; index < fHelpers.count(); index++) {
+        SkDisplayable* helper = fHelpers[index];
+        result = SkTSearch(children.begin(), children.count(), helper, sizeof(SkDisplayable*));
+        SkASSERT(result < 0);
+    }
+#endif
+    for (index = 0; index < fChildren.count(); index++) {
+        SkDisplayable* child = fChildren[index];
+        delete child;
+    }
+    for (index = 0; index < fHelpers.count(); index++) {
+        SkDisplayable* helper = fHelpers[index];
+        delete helper;
+    }
+    for (index = 0; index < fExtras.count(); index++) {
+        SkExtras* extras = fExtras[index];
+        delete extras;
+    }
+}
+
+void SkAnimateMaker::doDelayedEvent() {
+    fEnableTime = getAppTime();
+    for (int index = 0; index < fDelayed.count(); ) {
+        SkDisplayable* child = fDelayed[index];
+        SkASSERT(child->isApply());
+        SkApply* apply = (SkApply*) child;
+        apply->interpolate(*this, fEnableTime);
+        if (apply->hasDelayedAnimator())
+            index++;
+        else
+            fDelayed.remove(index);
+    }
+}
+
+bool SkAnimateMaker::doEvent(const SkEvent& event) {
+    return (!fInMovie || fLoaded) && fAnimator->doEvent(event);
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkAnimateMaker::dump(const char* match) {
+        SkTDict<SkDisplayable*>::Iter iter(fIDs);
+        const char* name;
+        SkDisplayable* result;
+        while ((name = iter.next(&result)) != NULL) {
+            if (strcmp(match,name) == 0)
+                result->dump(this);
+        }
+}
+#endif
+
+int SkAnimateMaker::dynamicProperty(SkString& nameStr, SkDisplayable** displayablePtr ) {
+    const char* name = nameStr.c_str();
+    const char* dot = strchr(name, '.');
+    SkASSERT(dot);
+    SkDisplayable* displayable;
+    if (find(name, dot - name, &displayable) == false) {
+        SkASSERT(0);
+        return 0;
+    }
+    const char* fieldName = dot + 1;
+    const SkMemberInfo* memberInfo = displayable->getMember(fieldName);
+    *displayablePtr = displayable;
+    return (int) memberInfo->fOffset;
+}
+
+SkMSec SkAnimateMaker::getAppTime() const {
+    return fTimeline->getMSecs();
+}
+
+#ifdef SK_DEBUG
+SkAnimator* SkAnimateMaker::getRoot()
+{
+    SkAnimateMaker* maker = this;
+    while (maker->fParentMaker)
+        maker = maker->fParentMaker;
+    return maker == this ? NULL : maker->fAnimator;
+}
+#endif
+
+void SkAnimateMaker::helperAdd(SkDisplayable* trackMe) {
+    SkASSERT(fHelpers.find(trackMe) < 0);
+    *fHelpers.append() = trackMe;
+}
+
+void SkAnimateMaker::helperRemove(SkDisplayable* alreadyTracked) {
+    int helperIndex = fHelpers.find(alreadyTracked);
+    if (helperIndex >= 0)
+        fHelpers.remove(helperIndex);
+}
+
+#if 0
+void SkAnimateMaker::loadMovies() {
+    for (SkDisplayable** dispPtr = fMovies.begin(); dispPtr < fMovies.end(); dispPtr++) {
+        SkDisplayable* displayable = *dispPtr;
+        SkASSERT(displayable->getType() == SkType_Movie);
+        SkDisplayMovie* movie = (SkDisplayMovie*) displayable;
+        SkAnimateMaker* movieMaker = movie->fMovie.fMaker;
+        movieMaker->fEvents.doEvent(*movieMaker, SkDisplayEvent::kOnload, NULL);
+        movieMaker->fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
+        movieMaker->loadMovies();
+    }
+}
+#endif
+
+void SkAnimateMaker::notifyInval() {
+    if (fHostEventSinkID)
+        fAnimator->onEventPost(new SkEvent(SK_EventType_Inval), fHostEventSinkID);
+}
+
+void SkAnimateMaker::notifyInvalTime(SkMSec time) {
+    if (fHostEventSinkID)
+        fAnimator->onEventPostTime(new SkEvent(SK_EventType_Inval), fHostEventSinkID, time);
+}
+
+void SkAnimateMaker::postOnEnd(SkAnimateBase* animate, SkMSec end) {
+        SkEvent evt;
+        evt.setS32("time", animate->getStart() + end);
+        evt.setPtr("anim", animate);
+        evt.setType(SK_EventType_OnEnd);
+        SkEventSinkID sinkID = fAnimator->getSinkID();
+        fAnimator->onEventPost(new SkEvent(evt), sinkID);
+}
+
+void SkAnimateMaker::reset() {
+    deleteMembers();
+    fChildren.reset();
+    fHelpers.reset();
+    fIDs.reset();
+    fEvents.reset();
+    fDisplayList.hardReset();
+}
+
+void SkAnimateMaker::removeActive(SkActive* active) {
+    if (active == NULL)
+        return;
+    fDisplayList.remove(active);
+}
+
+bool SkAnimateMaker::resolveID(SkDisplayable* displayable, SkDisplayable* original) {
+    SkString newID;
+    bool success = computeID(original, NULL, &newID);
+    if (success)
+        setID(displayable, newID);
+    return success;
+}
+
+void SkAnimateMaker::setErrorString() {
+    fErrorString.reset();
+    if (fError.hasError()) {
+        SkString err;
+        if (fFileName.size() > 0)
+            fErrorString.set(fFileName.c_str());
+        else
+            fErrorString.set("screenplay error");
+        int line = fError.getLineNumber();
+        if (line >= 0) {
+            fErrorString.append(", ");
+            fErrorString.append("line ");
+            fErrorString.appendS32(line);
+        }
+        fErrorString.append(": ");
+        fError.getErrorString(&err);
+        fErrorString.append(err);
+#if defined SK_DEBUG
+        SkDebugf("%s\n", fErrorString.c_str());
+#endif
+    } 
+}
+
+void SkAnimateMaker::setEnableTime(SkMSec appTime, SkMSec expectedTime) {
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkString debugOut;
+    SkMSec time = getAppTime();
+    debugOut.appendS32(time - fDebugTimeBase);
+    debugOut.append(" set enable old enable=");
+    debugOut.appendS32(fEnableTime - fDebugTimeBase);
+    debugOut.append(" old adjust=");
+    debugOut.appendS32(fAdjustedStart);
+    debugOut.append(" new enable=");
+    debugOut.appendS32(expectedTime - fDebugTimeBase);
+    debugOut.append(" new adjust=");
+    debugOut.appendS32(appTime - expectedTime);
+    SkDebugf("%s\n", debugOut.c_str());
+#endif
+    fAdjustedStart = appTime - expectedTime;
+    fEnableTime = expectedTime;
+    SkDisplayable** firstMovie = fMovies.begin();
+    SkDisplayable** endMovie = fMovies.end();
+    for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) {
+        SkDisplayMovie* movie = (SkDisplayMovie*) *ptr;
+        movie->fMovie.fMaker->setEnableTime(appTime, expectedTime);
+    }
+}
+
+void SkAnimateMaker::setExtraPropertyCallBack(SkDisplayTypes type, 
+        SkScriptEngine::_propertyCallBack callBack, void* userStorage) {
+    SkExtras** end = fExtras.end();
+    for (SkExtras** extraPtr = fExtras.begin(); extraPtr < end; extraPtr++) {
+        SkExtras* extra = *extraPtr;
+        if (extra->definesType(type)) {
+            extra->fExtraCallBack = callBack;
+            extra->fExtraStorage = userStorage;
+            break;
+        }
+    }
+}
+
+void SkAnimateMaker::setID(SkDisplayable* displayable, const SkString& newID) {
+    fIDs.set(newID.c_str(), displayable);
+#ifdef SK_DEBUG
+    displayable->_id.set(newID);
+    displayable->id = displayable->_id.c_str();
+#endif
+}
+
+void SkAnimateMaker::setScriptError(const SkScriptEngine& engine) {
+    SkString errorString;
+#ifdef SK_DEBUG
+    engine.getErrorString(&errorString);
+#endif
+    setErrorNoun(errorString);
+    setErrorCode(SkDisplayXMLParserError::kErrorInScript);
+}
+
+bool SkAnimateMaker::GetStep(const char* token, size_t len, void* stepPtr, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("step", token, len)) {
+        value->fOperand.fS32 = *(int32_t*) stepPtr;
+        value->fType = SkType_Int;
+        return true;
+    }
+    return false;
+}
diff --git a/legacy/src/animator/SkAnimateMaker.h b/legacy/src/animator/SkAnimateMaker.h
new file mode 100644
index 0000000..f20a7d4
--- /dev/null
+++ b/legacy/src/animator/SkAnimateMaker.h
@@ -0,0 +1,161 @@
+
+/*
+ * 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 SkAnimateMaker_DEFINED
+#define SkAnimateMaker_DEFINED
+
+// #define SK_DEBUG_ANIMATION_TIMING
+
+#include "SkAnimator.h"
+#include "SkBitmap.h"
+#include "SkIntArray.h"
+#include "SkDisplayEvents.h"
+#include "SkDisplayList.h"
+#include "SkDisplayScreenplay.h"
+#include "SkDisplayXMLParser.h"
+#include "SkScript.h"
+#include "SkString.h"
+#include "SkTDict.h"
+
+// not sure where this little helper macro should go
+
+
+class SkActive;
+class SkAnimate;
+class SkCanvas;
+class SkDisplayable;
+class SkDrawable;
+class SkDump;
+class SkEvent;
+class SkEventSink;
+class SkExtras;
+class SkGroup;
+class SkPaint;
+class SkStream;
+
+class SkAnimateMaker {
+public:
+    SkAnimateMaker(SkAnimator* animator, SkCanvas* canvas, SkPaint* paint);
+    ~SkAnimateMaker();
+    void appendActive(SkActive* );
+    void childrenAdd(SkDisplayable* child) { *fChildren.append() = child; }
+    void clearExtraPropertyCallBack(SkDisplayTypes type);
+    bool computeID(SkDisplayable* displayable, SkDisplayable* parent, SkString* newID);
+    SkDisplayable* createInstance(const char name[], size_t len);
+    bool decodeStream(SkStream* stream);
+    bool decodeURI(const char uri[]);
+    void delayEnable(SkApply* apply, SkMSec time);
+    void doDelayedEvent();
+    bool doEvent(const SkEvent& event);
+#ifdef SK_DUMP_ENABLED
+    void dump(const char* match);
+#endif
+    int dynamicProperty(SkString& nameStr, SkDisplayable**  );
+    bool find(const char* str, SkDisplayable** displayablePtr) const { 
+        return fIDs.find(str, displayablePtr);
+    }
+    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) { 
+//      return fIDs.find(string.c_str(), displayablePtr);
+//  }
+    SkAnimator* getAnimator() { return fAnimator; }
+    SkMSec getAppTime() const; // call caller to get current time
+#ifdef SK_DEBUG
+    SkAnimator* getRoot();
+#endif
+    SkXMLParserError::ErrorCode getErrorCode() const { return fError.getErrorCode(); }
+    SkMSec getInTime() { return fDisplayList.getTime(); }
+    int getNativeCode() const { return fError.getNativeCode(); }
+    bool hasError() { return fError.hasError(); }
+    void helperAdd(SkDisplayable* trackMe);
+    void helperRemove(SkDisplayable* alreadyTracked);
+    void idsSet(const char* attrValue, size_t len, SkDisplayable* displayable) { 
+        fIDs.set(attrValue, len, displayable); }
+//  void loadMovies();
+    void notifyInval();
+    void notifyInvalTime(SkMSec time);
+    void postOnEnd(SkAnimateBase* animate, SkMSec end);
+    void removeActive(SkActive* );
+    void reset();
+    bool resolveID(SkDisplayable* displayable, SkDisplayable* original);
+    void setEnableTime(SkMSec appTime, SkMSec expectedTime);
+    void setErrorCode(SkXMLParserError::ErrorCode err) { if (fError.hasError() == false) fError.INHERITED::setCode(err); }
+    void setErrorCode(SkDisplayXMLParserError::ErrorCode err) { if (fError.hasError() == false) fError.setCode(err); }
+    void setErrorNoun(const SkString& str) { if (fError.hasError() == false) fError.setNoun(str); }
+    void setErrorString();
+    void setExtraPropertyCallBack(SkDisplayTypes type, SkScriptEngine::_propertyCallBack , void* userStorage);
+    void setID(SkDisplayable* displayable, const SkString& newID);
+    void setInnerError(SkAnimateMaker* maker, const SkString& str) { fError.setInnerError(maker, str); }
+    void setScriptError(const SkScriptEngine& );
+#ifdef SK_DEBUG
+    void validate() { fDisplayList.validate(); }
+#else
+    void validate() {}
+#endif
+    SkDisplayEvent* fActiveEvent;
+    SkMSec fAdjustedStart;
+    SkCanvas* fCanvas;
+    SkMSec fEnableTime;
+    int fEndDepth;  // passed parameter to onEndElement
+    SkEvents fEvents;
+    SkDisplayList fDisplayList;
+    SkEventSinkID fHostEventSinkID;
+    SkMSec fMinimumInterval;
+    SkPaint* fPaint;
+    SkAnimateMaker* fParentMaker;
+    SkString fPrefix;
+    SkDisplayScreenplay fScreenplay;
+    const SkAnimator::Timeline* fTimeline;
+    SkBool8 fInInclude;
+    SkBool8 fInMovie;
+    SkBool8 fFirstScriptError;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkMSec fDebugTimeBase;
+#endif
+#ifdef SK_DUMP_ENABLED
+    SkString fDumpAnimated;
+    SkBool8 fDumpEvents;
+    SkBool8 fDumpGConditions;
+    SkBool8 fDumpPosts;
+#endif
+private:
+    void deleteMembers();
+    static bool GetStep(const char* token, size_t len, void* stepPtr, SkScriptValue* );
+    SkAnimateMaker& operator=(SkAnimateMaker& );
+    SkTDDisplayableArray fChildren;
+    SkTDDisplayableArray fDelayed; // SkApply that contain delayed enable events
+    SkDisplayXMLParserError fError;
+    SkString fErrorString;
+    SkTDArray<SkExtras*> fExtras;
+    SkString fFileName;
+    SkTDDisplayableArray fHelpers;  // helper displayables
+    SkBool8 fLoaded;
+    SkTDDisplayableArray fMovies;
+    SkTDict<SkDisplayable*> fIDs;
+    SkAnimator* fAnimator;
+    friend class SkAdd;
+    friend class SkAnimateBase;
+    friend class SkDisplayXMLParser;
+    friend class SkAnimator;
+    friend class SkAnimatorScript;
+    friend class SkApply;
+    friend class SkDisplayMovie;
+    friend class SkDisplayType;
+    friend class SkEvents;
+    friend class SkGroup;
+    friend struct SkMemberInfo;
+};
+
+#endif // SkAnimateMaker_DEFINED
+
diff --git a/legacy/src/animator/SkAnimateProperties.h b/legacy/src/animator/SkAnimateProperties.h
new file mode 100644
index 0000000..b070640
--- /dev/null
+++ b/legacy/src/animator/SkAnimateProperties.h
@@ -0,0 +1,21 @@
+
+/*
+ * 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 SkAnimateProperties_DEFINED
+#define SkAnimateProperties_DEFINED
+
+enum SkAnimateBase_Properties {
+    SK_PROPERTY(dynamic),
+    SK_PROPERTY(mirror),
+    SK_PROPERTY(reset),
+    SK_PROPERTY(step),
+    SK_PROPERTY(values)
+};
+
+#endif // SkAnimateProperties_DEFINED
diff --git a/legacy/src/animator/SkAnimateSchema.xsd b/legacy/src/animator/SkAnimateSchema.xsd
new file mode 100644
index 0000000..f7af332
--- /dev/null
+++ b/legacy/src/animator/SkAnimateSchema.xsd
@@ -0,0 +1,2787 @@
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+xmlns:Sk="urn:screenplay" targetNamespace="urn:screenplay">
+
+	<!-- /** Animate
+		An ID of an element of type <animate> or <set> 
+	*/ -->
+	<xs:simpleType name="Animate">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+
+	<!-- /** 3D_Point
+		An array of three floats in ECMAScript notation: [x, y, z].
+	*/ -->
+	<xs:simpleType name="3D_Point">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *, *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)){2}" /> 
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** ARGB
+		The red, green, blue, and optional alpha color components.
+	*/ -->
+	<xs:simpleType name="ARGB">
+		<xs:restriction base="xs:string">
+		<!-- @pattern #[0-9a-fA-F]{3} #rgb contains three hexadecimal digits. #rgb is equivalent to 0xFFrrggbb. -->
+			<xs:pattern value="#[0-9a-fA-F]{3}"/>
+		<!-- @pattern #[0-9a-fA-F]{4} #argb contains four hexadecimal digits. #argb is equivalent to 0xaarrggbb. -->
+			<xs:pattern value="#[0-9a-fA-F]{4}"/>
+		<!-- @pattern #[0-9a-fA-F]{6} #rrggbb contains six hexadecimal digits. #rrggbb is equivalent to 0xFFrrggbb. -->
+			<xs:pattern value="#[0-9a-fA-F]{6}"/>
+		<!-- @pattern #[0-9a-fA-F]{8} #aarrggbb contains eight hexadecimal digits. #aarrggbb is equivalent to 0xaarrggbb. -->
+			<xs:pattern value="#[0-9a-fA-F]{8}"/>
+		<!-- @pattern 0[xX][0-9a-fA-F]{8} 0xaarrggbb describes the color as a packed hexadecimal; each pair of digits 
+			corresponds to alpha, red, green, and blue respectively. -->
+			<xs:pattern value="0[xX][0-9a-fA-F]{8}"/>
+		<!-- @pattern rgb\(\d+{1,3},\d+{1,3},\d+{1,3}\) rgb(r, g, b) describes color with three integers ranging from 0 to 255, 
+			corresponding to red, green, and blue respectively. -->
+			<xs:pattern value="rgb\(\d+{1,3},\d+{1,3},\d+{1,3}\)"/>
+		<!-- @patternList Color can be described by the following standard CSS color names. -->
+			<xs:pattern value="aliceblue"/>
+			<xs:pattern value="antiquewhite"/>
+			<xs:pattern value="aqua"/>
+			<xs:pattern value="aquamarine"/>
+			<xs:pattern value="azure"/>
+			<xs:pattern value="beige"/>
+			<xs:pattern value="bisque"/>
+			<xs:pattern value="black"/>
+			<xs:pattern value="blanchedalmond"/>
+			<xs:pattern value="blue"/>
+			<xs:pattern value="blueviolet"/>
+			<xs:pattern value="brown"/>
+			<xs:pattern value="burlywood"/>
+			<xs:pattern value="cadetblue"/>
+			<xs:pattern value="chartreuse"/>
+			<xs:pattern value="chocolate"/>
+			<xs:pattern value="coral"/>
+			<xs:pattern value="cornflowerblue"/>
+			<xs:pattern value="cornsilk"/>
+			<xs:pattern value="crimson"/>
+			<xs:pattern value="cyan"/>
+			<xs:pattern value="darkblue"/>
+			<xs:pattern value="darkcyan"/>
+			<xs:pattern value="darkgoldenrod"/>
+			<xs:pattern value="darkgray"/>
+			<xs:pattern value="darkgreen"/>
+			<xs:pattern value="darkkhaki"/>
+			<xs:pattern value="darkmagenta"/>
+			<xs:pattern value="darkolivegreen"/>
+			<xs:pattern value="darkorange"/>
+			<xs:pattern value="darkorchid"/>
+			<xs:pattern value="darkred"/>
+			<xs:pattern value="darksalmon"/>
+			<xs:pattern value="darkseagreen"/>
+			<xs:pattern value="darkslateblue"/>
+			<xs:pattern value="darkslategray"/>
+			<xs:pattern value="darkturquoise"/>
+			<xs:pattern value="darkviolet"/>
+			<xs:pattern value="deeppink"/>
+			<xs:pattern value="deepskyblue"/>
+			<xs:pattern value="dimgray"/>
+			<xs:pattern value="dodgerblue"/>
+			<xs:pattern value="firebrick"/>
+			<xs:pattern value="floralwhite"/>
+			<xs:pattern value="forestgreen"/>
+			<xs:pattern value="fuchsia"/>
+			<xs:pattern value="gainsboro"/>
+			<xs:pattern value="ghostwhite"/>
+			<xs:pattern value="gold"/>
+			<xs:pattern value="goldenrod"/>
+			<xs:pattern value="gray"/>
+			<xs:pattern value="green"/>
+			<xs:pattern value="greenyellow"/>
+			<xs:pattern value="honeydew"/>
+			<xs:pattern value="hotpink"/>
+			<xs:pattern value="indianred"/>
+			<xs:pattern value="indigo"/>
+			<xs:pattern value="ivory"/>
+			<xs:pattern value="khaki"/>
+			<xs:pattern value="lavender"/>
+			<xs:pattern value="lavenderblush"/>
+			<xs:pattern value="lawngreen"/>
+			<xs:pattern value="lemonchiffon"/>
+			<xs:pattern value="lightblue"/>
+			<xs:pattern value="lightcoral"/>
+			<xs:pattern value="lightcyan"/>
+			<xs:pattern value="lightgoldenrodyellow"/>
+			<xs:pattern value="lightgreen"/>
+			<xs:pattern value="lightgrey"/>
+			<xs:pattern value="lightpink"/>
+			<xs:pattern value="lightsalmon"/>
+			<xs:pattern value="lightseagreen"/>
+			<xs:pattern value="lightskyblue"/>
+			<xs:pattern value="lightslategray"/>
+			<xs:pattern value="lightsteelblue"/>
+			<xs:pattern value="lightyellow"/>
+			<xs:pattern value="lime"/>
+			<xs:pattern value="limegreen"/>
+			<xs:pattern value="linen"/>
+			<xs:pattern value="magenta"/>
+			<xs:pattern value="maroon"/>
+			<xs:pattern value="mediumaquamarine"/>
+			<xs:pattern value="mediumblue"/>
+			<xs:pattern value="mediumorchid"/>
+			<xs:pattern value="mediumpurple"/>
+			<xs:pattern value="mediumseagreen"/>
+			<xs:pattern value="mediumslateblue"/>
+			<xs:pattern value="mediumspringgreen"/>
+			<xs:pattern value="mediumturquoise"/>
+			<xs:pattern value="mediumvioletred"/>
+			<xs:pattern value="midnightblue"/>
+			<xs:pattern value="mintcream"/>
+			<xs:pattern value="mistyrose"/>
+			<xs:pattern value="moccasin"/>
+			<xs:pattern value="navajowhite"/>
+			<xs:pattern value="navy"/>
+			<xs:pattern value="oldlace"/>
+			<xs:pattern value="olive"/>
+			<xs:pattern value="olivedrab"/>
+			<xs:pattern value="orange"/>
+			<xs:pattern value="orangered"/>
+			<xs:pattern value="orchid"/>
+			<xs:pattern value="palegoldenrod"/>
+			<xs:pattern value="palegreen"/>
+			<xs:pattern value="paleturquoise"/>
+			<xs:pattern value="palevioletred"/>
+			<xs:pattern value="papayawhip"/>
+			<xs:pattern value="peachpuff"/>
+			<xs:pattern value="peru"/>
+			<xs:pattern value="pink"/>
+			<xs:pattern value="plum"/>
+			<xs:pattern value="powderblue"/>
+			<xs:pattern value="purple"/>
+			<xs:pattern value="red"/>
+			<xs:pattern value="rosybrown"/>
+			<xs:pattern value="royalblue"/>
+			<xs:pattern value="saddlebrown"/>
+			<xs:pattern value="salmon"/>
+			<xs:pattern value="sandybrown"/>
+			<xs:pattern value="seagreen"/>
+			<xs:pattern value="seashell"/>
+			<xs:pattern value="sienna"/>
+			<xs:pattern value="silver"/>
+			<xs:pattern value="skyblue"/>
+			<xs:pattern value="slateblue"/>
+			<xs:pattern value="slategray"/>
+			<xs:pattern value="snow"/>
+			<xs:pattern value="springgreen"/>
+			<xs:pattern value="steelblue"/>
+			<xs:pattern value="tan"/>
+			<xs:pattern value="teal"/>
+			<xs:pattern value="thistle"/>
+			<xs:pattern value="tomato"/>
+			<xs:pattern value="turquoise"/>
+			<xs:pattern value="violet"/>
+			<xs:pattern value="wheat"/>
+			<xs:pattern value="white"/>
+			<xs:pattern value="whitesmoke"/>
+			<xs:pattern value="yellow"/>
+		<!--@patternListLast -->
+			<xs:pattern value="yellowgreen"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** AddMode
+		AddMode controls how the add element adds its referenced element to the 
+		display list. By default, the referenced element remains in the add element
+		so that the add element's use attribute may be animated to change the 
+		element it refers to. Setting the mode attribute to "immediate" causes the 
+		add element to put the referenced element in the display list directly. 
+		The move and replace elements are not affected by the mode attribute;
+		they always move or replace the referenced element directly.
+	*/ -->
+	<xs:simpleType name="AddMode">
+		<xs:restriction base="xs:string">
+			<!-- @pattern immediate Puts the referenced element in the display list. -->
+			<xs:pattern value="immediate"/>
+			<!-- @pattern indirect Puts the containing element in the display list. -->
+			<xs:pattern value="indirect"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Align
+		Align places text to the left, center, or right of the text position.
+	*/ -->
+	<xs:simpleType name="Align">
+		<xs:restriction base="xs:string">
+			<!-- @pattern left The first character in the text string is drawn at the text position. -->
+			<xs:pattern value="left"/>
+			<!-- @pattern center The  text string is measured and centered on the text position. -->
+			<xs:pattern value="center"/>
+			<!-- @pattern right The last character in the text string is drawn to the left of the text position. -->
+			<xs:pattern value="right"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** ApplyMode
+		ApplyMode affects how the apply element animates values.
+	*/ -->
+	<xs:simpleType name="ApplyMode">
+		<xs:restriction base="xs:string">
+			<!-- @pattern immediate Iterates through all animation values immediately. -->
+			<xs:pattern value="immediate"/>
+			<!-- @pattern once Performs the animation at once without adding the scope to
+				the display list. -->
+			<xs:pattern value="once"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** ApplyTransition
+		ApplyTransition affects how the apply element sets the time of the animators.
+	*/ -->
+	<xs:simpleType name="ApplyTransition">
+		<xs:restriction base="xs:string">
+			<!-- @pattern reverse Performs the animation in reverse. -->
+			<xs:pattern value="reverse"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Base64
+		Base64 describes 8 bit binary using 64 character values. 
+		See http://rfc.net/rfc2045.html for the base64 format.
+	*/ -->
+	<xs:simpleType name="Base64">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="[A-Za-z0-9+/ ]+"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** BaseBitmap
+		A reference to an image like a JPEG, GIF, or PNG; or a reference to a bitmap element
+		that has been drawn into with a drawTo element.
+	*/ -->
+	<xs:simpleType name="BaseBitmap">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** BitmapEncoding
+		Used to specify the compression format for writing an image file with the snapshot element.
+	*/ -->
+	<xs:simpleType name="BitmapEncoding">
+		<xs:restriction base="xs:string">
+			<!-- @pattern jpeg See http://www.jpeg.org/jpeg/ for more information about JPEG. -->
+			<xs:pattern value="jpeg"/>
+			<!-- @pattern png See http://www.libpng.org/pub/png/ for more information about PNG. -->
+			<xs:pattern value="png"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- /** BitmapFormat
+		Determines the number of bits per pixel in a bitmap.
+	*/ -->
+	<xs:simpleType name="BitmapFormat">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="none"/>
+			<!-- @pattern A1 1-bit per pixel, (0 is transparent, 1 is opaque). -->
+			<xs:pattern value="A1"/>
+			<!-- @pattern A8 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque). -->
+			<xs:pattern value="A8"/>
+			<!-- @pattern Index8 8-bits per pixel, using a ColorTable element to specify the colors. -->
+			<xs:pattern value="Index8"/>
+			<!-- @pattern RGB16 16-bits per pixel, compile-time configured to be either 555 or 565. -->
+			<xs:pattern value="RGB16"/>
+			<!-- @pattern RGB32 32-bits per pixel, plus alpha. -->
+			<xs:pattern value="RGB32"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Boolean
+		Either "true" (non-zero) or "false" (zero). 
+	*/ -->
+	<xs:simpleType name="Boolean">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="false"/>
+			<xs:pattern value="true"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Cap
+		The values for the strokeCap attribute. 
+	*/ -->
+	<xs:simpleType name="Cap">
+		<xs:restriction base="xs:string">
+			<!-- @pattern butt begin and end a contour with no extension -->
+			<xs:pattern value="butt"/>
+			<!-- @pattern round begin and end a contour with a semi-circle extension -->
+			<xs:pattern value="round"/>
+			<!-- @pattern  square begin and end a contour with a half square extension -->
+			<xs:pattern value="square"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Color
+		A reference to a color element. 
+	*/ -->
+	<xs:simpleType name="Color">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** Displayable
+		A reference to any element: @list(Displayable)
+	*/ -->
+	<xs:simpleType name="Displayable">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+
+	<!-- /** DisplayableArray
+		An array of one or more element IDs.
+	*/ -->
+	<xs:simpleType name="DisplayableArray">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+
+	<!-- /** Drawable
+		A reference to an element that can be drawn: @list(Drawable)
+	*/ -->
+	<xs:simpleType name="Drawable">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+
+	<!-- /** DynamicString
+		Dynamic strings contain scripts that are re-evaluated each time the script is enabled.
+	*/ -->
+	<xs:simpleType name="DynamicString">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** EventCode
+		Key codes that can trigger events, usually corresponding to physical buttons on the device.
+	*/ -->
+	<xs:simpleType name="EventCode">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="none"/>
+			<!-- @pattern up The up arrow. -->
+			<xs:pattern value="up"/>
+			<!-- @pattern down The down arrow. -->
+			<xs:pattern value="down"/>
+			<!-- @pattern left The left arrow. -->
+			<xs:pattern value="left"/>
+			<!-- @pattern right The right arrow. -->
+			<xs:pattern value="right"/>
+			<!-- @pattern back The back button (may not be present; the Backspace key on a PC). -->
+			<xs:pattern value="back"/>
+			<!-- @pattern end The end button (may not be present; the Esc key on a PC). -->
+			<xs:pattern value="end"/>
+			<!-- @pattern OK The OK button (the Enter key on a PC). -->
+			<xs:pattern value="OK"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** EventKind
+		Specifies how an event is triggered; by a key, when an animation ends, when the 
+		document is loaded, or when this event is triggered by the user's C++ or XML.
+	*/ -->
+	<xs:simpleType name="EventKind">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="none"/>
+			<!-- @pattern keyChar A key corresponding to a Unichar value. -->
+			<xs:pattern value="keyChar"/>
+			<!-- @pattern keyPress A key with a particular function, such as an arrow key or the OK button. -->
+			<xs:pattern value="keyPress"/>
+			<!-- @pattern mouseDown Triggered when the primary mouse button is pressed. -->
+			<xs:pattern value="mouseDown"/>
+			<!-- @pattern mouseDrag Triggered when the primary mouse is moved while the button is pressed. -->
+			<xs:pattern value="mouseDrag"/>
+			<!-- @pattern mouseMove Triggered when the primary mouse is moved. -->
+			<xs:pattern value="mouseMove"/>
+			<!-- @pattern mouseUp Triggered when the primary mouse button is released. -->
+			<xs:pattern value="mouseUp"/>
+			<!-- @pattern onEnd Triggered when an event ends. -->
+			<xs:pattern value="onEnd"/>
+			<!-- @pattern onLoad Triggered when the document loads. -->
+			<xs:pattern value="onLoad"/>
+			<!-- @pattern user Triggered when a post element or C++ event is activated. -->
+			<xs:pattern value="user"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** EventMode
+		Specifies whether the event is delivered immediately to matching event element or deferred to 
+		the application-wide event handler.
+	*/ -->
+	<xs:simpleType name="EventMode">
+		<xs:restriction base="xs:string">
+			<!-- @pattern deferred Process the event using the host's event queue. -->
+			<xs:pattern value="deferred"/>
+			<!-- @pattern immediate Activate the event element immediately. -->
+			<xs:pattern value="immediate"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- /** FillType
+		Filled paths that self-intersect use the winding or evenOdd rule to determine whether the 
+		overlaps are filled or are holes.
+	*/ -->
+	<xs:simpleType name="FillType">
+		<xs:restriction base="xs:string">
+			<!-- @pattern winding Fill if the sum of edge directions is non-zero. -->
+			<xs:pattern value="winding"/>
+			<!-- @pattern evenOdd Fill if the sum of edges is an odd number. -->
+			<xs:pattern value="evenOdd"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** FilterType
+		Scaled bitmaps without a filter type set point-sample the source bitmap to determine the
+		destination pixels' colors. Bilinear and bicubic compute the values of intermediate pixels
+		by sampling the pixels around them.
+	*/ -->
+	<xs:simpleType name="FilterType">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="none"/>
+			<!-- @pattern bilinear Compute the pixel value as the linear interpolation of adjacent pixels. -->
+			<xs:pattern value="bilinear"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Float
+		A signed fractional value.
+	*/ -->
+	<xs:simpleType name="Float">
+		<xs:restriction base="xs:float">
+			<xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** FloatArray
+		An array of one or more signed fractional values.
+	*/ -->
+	<xs:simpleType name="FloatArray">
+		<xs:restriction base="xs:float">
+			<xs:pattern value="\[[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *, *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?))*\]"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** FromPathMode
+		A matrix computed from an offset along a path may include the point's position, the angle 
+		tangent, or both. 
+		.
+	*/ -->
+	<xs:simpleType name="FromPathMode">
+		<xs:restriction base="xs:string">
+			<!-- @pattern normal Compute the matrix using the path's angle and position. -->
+			<xs:pattern value="normal"/>
+			<!-- @pattern angle Compute the matrix using only the path's angle. -->
+			<xs:pattern value="angle"/>
+			<!-- @pattern position Compute the matrix using only the path's position. -->
+			<xs:pattern value="position"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- /** Int
+		A signed integer.
+	*/ -->
+	<xs:simpleType name="Int">
+		<xs:restriction base="xs:integer"/>
+	</xs:simpleType>
+	
+	<!-- /** IntArray
+		An array of one or more signed integer values.
+	*/ -->
+	<xs:simpleType name="IntArray">
+		<xs:restriction base="xs:integer">
+			<xs:pattern value="\[[+-]?[0-9]+( *, *[+-]?[0-9]+)*\]"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Join
+		The edges of thick lines in a path are joined by extending the outer edges to form a miter, 
+		or by adding a round circle at the intersection point, or by connecting the outer edges with a line 
+		to form a blunt joint.
+	*/ -->
+	<xs:simpleType name="Join">
+		<xs:restriction base="xs:string">
+			<!-- @pattern miter Extend the outer edges to form a miter. -->
+			<xs:pattern value="miter"/>
+			<!-- @pattern round Join the outer edges with a circular arc. -->
+			<xs:pattern value="round"/>
+			<!-- @pattern blunt Connect the outer edges with a line. -->
+			<xs:pattern value="blunt"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** MaskFilterBlurStyle
+		A blur can affect the inside or outside part of the shape, or it can affect both. The shape
+		itself can be drawn solid, or can be invisible.
+	*/ -->
+	<xs:simpleType name="MaskFilterBlurStyle">
+		<xs:restriction base="xs:string">
+			<!-- @pattern normal Blur inside and outside. -->
+			<xs:pattern value="normal"/>
+			<!-- @pattern solid Solid inside, blur outside. -->
+			<xs:pattern value="solid"/>
+			<!-- @pattern outer Invisible inside, blur outside. -->
+			<xs:pattern value="outer"/>
+			<!-- @pattern inner Blur inside only.. -->
+			<xs:pattern value="inner"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** MaskFilter
+		The ID of a blur or emboss element.
+	*/ -->
+	<xs:simpleType name="MaskFilter">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** Matrix
+		The ID of a matrix element.
+	*/ -->
+	<xs:simpleType name="Matrix">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** MSec
+		A fractional second with millisecond resolution.
+	*/ -->
+	<xs:simpleType name="MSec">
+		<xs:restriction base="xs:float"/>
+	</xs:simpleType>
+	
+	<!-- /** Paint
+		The ID of a paint element.
+	*/ -->
+	<xs:simpleType name="Paint">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+
+	<!-- /** Path
+		The ID of a path element.
+	*/ -->
+	<xs:simpleType name="Path">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** PathDirection
+		PathDirection determines if the path is traveled clockwise or counterclockwise.
+	*/ -->
+	<xs:simpleType name="PathDirection">
+		<xs:restriction base="xs:string">
+		<!-- @pattern cw The path is traveled clockwise. -->
+			<xs:pattern value="cw"/>
+		<!-- @pattern ccw The path is traveled counterclockwise. -->
+			<xs:pattern value="ccw"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** PathEffect
+		The ID of a dash or discrete element.
+	*/ -->
+	<xs:simpleType name="PathEffect">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** Point
+		A pair of signed values representing the x and y coordinates of a point.
+	*/ -->
+	<xs:simpleType name="Point">
+		<xs:restriction base="xs:string">
+			<xs:pattern value="\[ *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?) *[ ,] *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)\]"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Rect
+		The ID of a rectangle element.
+	*/ -->
+	<xs:simpleType name="Rect">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** Shader
+		The ID of a linear or radial gradient.
+	*/ -->
+	<xs:simpleType name="Shader">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** String
+		A sequence of characters.
+	*/ -->
+	<xs:simpleType name="String">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** Style
+		Geometry can be filled, stroked or both.
+	*/ -->
+	<xs:simpleType name="Style">
+		<xs:restriction base="xs:string">
+		<!-- @pattern fill The interior of the geometry is filled with the paint's color. -->
+			<xs:pattern value="fill"/>
+		<!-- @pattern stroke The outline of the geometry is stroked with the paint's color. -->
+			<xs:pattern value="stroke"/>
+		<!-- @pattern strokeAndFill The interior is filled and outline is stroked with the paint's color. -->
+			<xs:pattern value="strokeAndFill"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Text
+		The ID of a text element.
+	*/ -->
+	<xs:simpleType name="Text">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** TextBoxAlign
+		Multiple lines of text may be aligned to the start of the box, the center, or the end.
+	*/ -->
+	<xs:simpleType name="TextBoxAlign">
+		<xs:restriction base="xs:string">
+		<!-- @pattern start The text begins within the upper left of the box. -->
+			<xs:pattern value="start"/>
+		<!-- @pattern center The text is positioned in the center of the box. -->
+			<xs:pattern value="center"/>
+		<!-- @pattern end The text ends within the lower right of the box. -->
+			<xs:pattern value="end"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- /** TextBoxMode
+		Fitting the text may optionally introduce line breaks.
+	*/ -->
+	<xs:simpleType name="TextBoxMode">
+		<xs:restriction base="xs:string">
+		<!-- @pattern oneLine No additional linebreaks are added. -->
+			<xs:pattern value="oneLine"/>
+		<!-- @pattern lineBreak Line breaks may be added to fit the text to the box. -->
+			<xs:pattern value="lineBreak"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** TileMode
+		A shader describes how to draw within a rectangle. 
+		Outside of the rectangle, the shader may be ignored, clamped on the edges, or repeated.
+		The repetitions may be mirrored from the original shader.
+	*/ -->
+	<xs:simpleType name="TileMode">
+		<xs:restriction base="xs:string">
+		<!-- @pattern clamp The edge shader color is extended. -->
+			<xs:pattern value="clamp"/>
+		<!-- @pattern repeat The shader is repeated horizontally and vertically. -->
+			<xs:pattern value="repeat"/>
+		<!-- @pattern mirror The shader is mirrored horizontally and vertically. -->
+			<xs:pattern value="mirror"/>
+		</xs:restriction>
+	</xs:simpleType>
+	
+	<!-- /** Typeface
+		The ID of a typeface element.
+	*/ -->
+	<xs:simpleType name="Typeface">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+	
+	<!-- /** UnknownArray
+		An array of values of any type.
+	*/ -->
+	<xs:simpleType name="UnknownArray">
+		<xs:restriction base="xs:string"/>
+	</xs:simpleType>
+
+	<!-- /** Xfermode
+		The operation applied when drawing a color to the destination background.
+	*/ -->
+	<xs:simpleType name="Xfermode">
+		<xs:restriction base="xs:string">
+		<!-- @pattern clear Set the destination alpha to zero and the destination color to black. -->
+			<xs:pattern value="clear"/>
+		<!-- @pattern src Set the destination to the source alpha and color. -->
+			<xs:pattern value="src"/>
+		<!-- @pattern dst Set the destination to the destination alpha and color. -->
+			<xs:pattern value="dst"/>
+		<!-- @pattern srcOver The default. Set the destination to the source color blended
+			with the destination by the source alpha. -->
+			<xs:pattern value="srcOver"/>
+		<!-- @pattern dstOver Set the destination to the destination color blended
+			with the source by the destination alpha. -->
+			<xs:pattern value="dstOver"/>
+		<!-- @pattern srcIn Set the destination to the source color scaled by the destination
+			alpha. -->
+			<xs:pattern value="srcIn"/>
+		<!-- @pattern dstIn Set the destination to the destination color scaled by the source
+			alpha. -->
+			<xs:pattern value="dstIn"/>
+		<!-- @pattern srcOut Set the destination to the source color scaled by the 
+			inverse of the destination alpha. -->
+			<xs:pattern value="srcOut"/>
+		<!-- @pattern dstOut Set the destination to the destination color scaled by the 
+			inverse of the source alpha. -->
+			<xs:pattern value="dstOut"/>
+		<!-- @pattern srcATop Set the destination to the source color times the destination alpha, 
+			blended	with the destination times the inverse of the source alpha. -->
+			<xs:pattern value="srcATop"/>
+		<!-- @pattern dstATop Set the destination to the destination color times the source alpha, 
+			blended	with the source times the inverse of the destination alpha. -->
+			<xs:pattern value="dstATop"/>
+		<!-- @pattern xor Set the destination to the destination color times the 
+			inverse of the source alpha, 
+			blended	with the source times the inverse of the destination alpha. -->
+			<xs:pattern value="xor"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- /** Math
+		Math provides functions and properties in the ECMAScript library to screenplay script expressions.
+		The Math element is always implicitly added at the top of every screenplay description, so
+		its functions and properties are always available.
+	*/ -->
+	<xs:element name="Math">
+		<xs:complexType>
+			<!-- @attribute E The value 2.718281828. -->
+			<xs:attribute name="E" type="Sk:Float"/>
+			<!-- @attribute LN10 The value 2.302585093. -->
+			<xs:attribute name="LN10" type="Sk:Float"/>
+			<!-- @attribute LN2 The value 0.693147181. -->
+			<xs:attribute name="LN2" type="Sk:Float"/>
+			<!-- @attribute LOG10E The value 0.434294482. -->
+			<xs:attribute name="LOG10E" type="Sk:Float"/>
+			<!-- @attribute LOG2E The value 1.442695041. -->
+			<xs:attribute name="LOG2E" type="Sk:Float"/>
+			<!-- @attribute PI The value 3.141592654. -->
+			<xs:attribute name="PI" type="Sk:Float"/>
+			<!-- @attribute SQRT1_2 The value 0.707106781. -->
+			<xs:attribute name="SQRT1_2" type="Sk:Float"/>
+			<!-- @attribute SQRT2 The value 1.414213562. -->
+			<xs:attribute name="SQRT2" type="Sk:Float"/>
+			<!-- @attribute abs A function that returns the absolute value of its argument. -->
+			<xs:attribute name="abs" type="Sk:Float"/>
+			<!-- @attribute acos A function that returns the arc cosine of its argument. -->
+			<xs:attribute name="acos" type="Sk:Float"/>
+			<!-- @attribute asin A function that returns the arc sine of its argument. -->
+			<xs:attribute name="asin" type="Sk:Float"/>
+			<!-- @attribute atan A function that returns the arc tan of its argument. -->
+			<xs:attribute name="atan" type="Sk:Float"/>
+			<!-- @attribute atan2 A function that returns the arc tan of the ratio of its two arguments. -->
+			<xs:attribute name="atan2" type="Sk:Float"/>
+			<!-- @attribute ceil A function that returns the rounded up value of its argument. -->
+			<xs:attribute name="ceil" type="Sk:Float"/>
+			<!-- @attribute cos A function that returns the cosine of its argument. -->
+			<xs:attribute name="cos" type="Sk:Float"/>
+			<!-- @attribute exp A function that returns E raised to a power (the argument). -->
+			<xs:attribute name="exp" type="Sk:Float"/>
+			<!-- @attribute floor A function that returns the rounded down value of its argument. -->
+			<xs:attribute name="floor" type="Sk:Float"/>
+			<!-- @attribute log A function that returns the natural logarithm its argument. -->
+			<xs:attribute name="log" type="Sk:Float"/>
+			<!-- @attribute max A function that returns the largest of any number of arguments. -->
+			<xs:attribute name="max" type="Sk:Float"/>
+			<!-- @attribute min A function that returns the smallest of any number of arguments. -->
+			<xs:attribute name="min" type="Sk:Float"/>
+			<!-- @attribute pow A function that returns the first argument raised to the power of the second argument. -->
+			<xs:attribute name="pow" type="Sk:Float"/>
+			<!-- @attribute random A function that returns a random value from zero to one.
+				(See also the &lt;random&gt; element.) -->
+			<xs:attribute name="random" type="Sk:Float"/>
+			<!-- @attribute round A function that returns the rounded value of its argument. -->
+			<xs:attribute name="round" type="Sk:Float"/>
+			<!-- @attribute sin A function that returns the sine of its argument. -->
+			<xs:attribute name="sin" type="Sk:Float"/>
+			<!-- @attribute sqrt A function that returns the square root of its argument. -->
+			<xs:attribute name="sqrt" type="Sk:Float"/>
+			<!-- @attribute tan A function that returns the tangent of its argument. -->
+			<xs:attribute name="tan" type="Sk:Float"/>	
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** Number
+		Number provides properties in the ECMAScript library to screenplay script expressions.
+		The Number element is always implicitly added at the top of every screenplay description, so
+		its properties are always available.
+	*/ -->
+	<xs:element name="Number">
+		<xs:complexType>
+			<!-- @attribute MAX_VALUE The maximum number value; approximately 32767.999985 fixed point, 
+				3.4028235e+38 floating point. -->
+			<xs:attribute name="MAX_VALUE" type="Sk:Float"/>
+			<!-- @attribute MIN_VALUE The minimum number value; approximately 0.000015 fixed point, 
+				1.1754944e-38 floating point. -->
+			<xs:attribute name="MIN_VALUE" type="Sk:Float"/>
+			<!-- @attribute NEGATIVE_INFINITY The most negative number value. Fixed point does not
+				have a value for negative infinity, and approximates it with -32767.999985. -->
+			<xs:attribute name="NEGATIVE_INFINITY" type="Sk:Float"/>
+			<!-- @attribute NaN A bit pattern representing "Not a Number". Fixed point does not
+				have a value for NaN, and approximates it with -32768. -->
+			<xs:attribute name="NaN" type="Sk:Float"/>
+			<!-- @attribute POSITIVE_INFINITY The greatest positive number value. Fixed point does not
+				have a value for positive infinity, and approximates it with 32767.999985. -->
+			<xs:attribute name="POSITIVE_INFINITY" type="Sk:Float"/>
+		</xs:complexType>
+	</xs:element>
+
+	<!-- /** add
+		Add references a drawable element, and adds it to the display list. 
+		If where and offset are omitted, the element is appended to the end of the display list.
+		If where is specified, the element is inserted at the first occurance of where in the display list.
+		If offset and where are specified, the element is inserted at where plus offset.
+		A positive offset without where inserts the element at the start of the list plus offset.
+		A negative offset without where inserts the element at the end of the list minus offset.
+	*/ -->
+	<xs:element name="add">
+		<xs:complexType>
+			<!-- @attribute mode If indirect (the default), keep the add element in the display list,
+				and draw the add's use element. If immediate, put the add's use element in the display list. -->
+			<xs:attribute name="mode" type="Sk:AddMode"/>
+			<!-- @attribute offset The offset added to the insert index. -->
+			<xs:attribute name="offset" type="Sk:Int"/>
+			<!-- @attribute use The drawable element to add to the display list. -->
+			<xs:attribute name="use" type="Sk:Drawable"/>
+			<!-- @attribute where The drawable element marking where to insert. -->
+			<xs:attribute name="where" type="Sk:Drawable"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** addCircle
+		AddCircle adds a closed circle to the parent path element. 
+	*/ -->
+	<xs:element name="addCircle">
+		<xs:complexType>
+			<!-- @attribute direction One of @pattern. @patternDescription -->
+			<xs:attribute name="direction" type="Sk:PathDirection"/>
+			<!-- @attribute radius The distance from the center to the edge of the circle. -->
+			<xs:attribute name="radius" type="Sk:Float"/>
+			<!-- @attribute x The x coordinate of the circle's center. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The y coordinate of the circle's center.-->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** addOval
+		AddOval adds a closed oval described by its bounding box to the parent path element. 
+	*/ -->
+	<xs:element name="addOval">
+		<xs:complexType>
+			<!-- @attribute direction One of @pattern. @patternDescription -->
+			<xs:attribute name="direction" type="Sk:PathDirection"/>
+			<!-- @attribute bottom The bottom edge of the oval's bounding box. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute left The left edge of the oval's bounding box. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute right The right edge of the oval's bounding box. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute top The top edge of the oval's bounding box. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** addPath
+		AddPath adds a path to the parent path element. 
+		An optional matrix may transform the path as it is added.
+	*/ -->
+	<xs:element name="addPath">
+		<xs:complexType>
+			<!-- @attribute matrix The matrix applied to the path as it is added.  -->
+			<xs:attribute name="matrix" type="Sk:Matrix"/>
+			<!-- @attribute path The path to add.  -->
+			<xs:attribute name="path" type="Sk:Path"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** addRect
+		AddRect adds a closed rectangle to the parent path element. 
+	*/ -->
+	<xs:element name="addRect">
+		<xs:complexType>
+			<!-- @attribute direction One of @pattern. @patternDescription -->
+			<xs:attribute name="direction" type="Sk:PathDirection"/>
+			<!-- @attribute bottom The bottom edge of the rectangle. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute left The left edge of the rectangle. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute right The right edge of the rectangle. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute top" The top" edge of the rectangle. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** addRoundRect
+		AddRoundRect adds a closed rounded rectangle to the parent path element. 
+	*/ -->
+	<xs:element name="addRoundRect">
+		<xs:complexType>
+			<!-- @attribute direction One of @pattern. @patternDescription -->
+			<xs:attribute name="direction" type="Sk:PathDirection"/>
+			<!-- @attribute bottom The bottom edge of the rounded rectangle's bounding box. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute left The left edge of the rounded rectangle's bounding box. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute right The right edge of the rounded rectangle's bounding box. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute top The top edge of the rounded rectangle's bounding box. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<!-- @attribute rx The X-radius of the oval used to round the corners. -->
+			<xs:attribute name="rx" type="Sk:Float"/>
+			<!-- @attribute ry The Y-radius of the oval used to round the corners. -->
+			<xs:attribute name="ry" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+		
+	<!-- /** animate
+		Animate varies the value of an element's attribute over time.
+		The animation may vary starting at the 'from' attribute, and ending at the 'to' attribute,
+		or may compute the value using the 'formula' attribute.
+	*/ -->
+	<xs:element name="animate">
+		<xs:complexType>
+			<!-- @attribute begin An optional offset that must elapse before the animation begins. The apply
+				begin attribute is added to any animator's begin attribute. -->
+			<xs:attribute name="begin" type="Sk:MSec"/>
+			<!-- @attribute blend Specifies how the from and to values are blended. A value from 0.0 to
+				1.0 specifies a cubic lag/log/lag blend (slow to change at the beginning and end); the closer
+				blend is to 1.0, the more linear the blend. If omitted, the blend is linear. -->
+			<xs:attribute name="blend" type="Sk:FloatArray"/>
+			<!-- @attribute dur The duration of the animation in milliseconds. -->
+			<xs:attribute name="dur" type="Sk:MSec"/>
+			<!-- @attribute dynamic If true, restart the animation if any of the simple values the 'from', 'formula',
+			 'lval', or 'to' attributes reference are changed. Simple values are contained by the array, boolean, float, int, 
+			 and string elements. -->
+			<xs:attribute name="dynamic" type="Sk:Boolean" />
+			<!-- @attribute field The attribute to animate. -->
+			<xs:attribute name="field" type="Sk:String"/>
+			<!-- @attribute formula A script to execute over time to compute the field's value. Typically,
+				the fomula is a script expression which includes a reference to the time attribute of the 
+				containing apply	element.  Requires a dur.  For animations that do not stop, set dur="Number.POSITIVE_INFINITY" -->
+			<xs:attribute name="formula" type="Sk:DynamicString"/>
+			<!-- @attribute from The starting value (requires a 'to' attribute) -->
+			<xs:attribute name="from" type="Sk:DynamicString"/>
+			<!-- @attribute lval An expression evaluating to the attribute to animate.
+				If present, lval overrides 'field'. The expression is typically an array element,
+				e.g. lval="x[y]" . -->
+			<xs:attribute name="lval" type="Sk:DynamicString"/>
+			<!-- @attribute mirror If true, reverses the interpolated value during even repeat cycles. -->
+			<xs:attribute name="mirror" type="Sk:Boolean"/>
+			<!-- @attribute repeat Specifies the number of times to repeat the animation. 
+				(May be fractional.)  -->
+			<xs:attribute name="repeat" type="Sk:Float"/>
+			<!-- @attribute reset  If true, the computed value is the initial value after the 
+				animation is complete. If false, or by default, the computed value is the final value 
+				after the animation is complete. -->
+			<xs:attribute name="reset" type="Sk:Boolean"/>
+			<!-- @attribute step When the apply's attribute mode="immediate" or "create", the step attribute can be read by 
+				script to determine the current animation iteration.  -->
+			<xs:attribute name="step" type="Sk:Int" />
+			<!-- @attribute target The element to animate. By default, the element contained by the apply
+				or referenced by the apply's scope attribute is the animate target. -->
+			<xs:attribute name="target" type="Sk:DynamicString"/>
+			<!-- @attribute to The ending value (requires a 'from' attribute) -->
+			<xs:attribute name="to" type="Sk:DynamicString"/>
+			<!-- @attribute values [Depreciated]  -->
+			<xs:attribute name="values" type="Sk:DynamicString"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+		
+	<!-- /** apply
+		Apply changes one or more attributes of an element.
+		Apply either contains one displayable element or references the element scoping the change
+		with the 'scope' attribute. Apply either contains one animator element or references it with 
+		the 'animator' attribute.
+		In the display list, apply draws the element it scopes after evaluating the animation.
+	*/ -->
+	<xs:element name="apply">
+		<xs:complexType>
+			<xs:choice minOccurs="0" maxOccurs="1">
+				<xs:element ref="Sk:animate"/>
+				<xs:element ref="Sk:set" />
+		<!-- not quite right; want to say 'one of the above, one of the below'
+			</xs:choice>
+			<xs:choice minOccurs="0" maxOccurs="1">
+		-->
+				<xs:element ref="Sk:add"/>
+				<xs:element ref="Sk:array"/>
+				<xs:element ref="Sk:apply"/>
+				<xs:element ref="Sk:bitmap"/>
+				<xs:element ref="Sk:boolean"/>
+				<xs:element ref="Sk:bounds"/>
+		<!--		<xs:element ref="Sk3D:camera"/>    -->
+				<xs:element ref="Sk:clear"/>
+				<xs:element ref="Sk:clip"/>
+				<xs:element ref="Sk:color"/>
+				<xs:element ref="Sk:drawTo"/>
+				<xs:element ref="Sk:float"/>
+				<xs:element ref="Sk:full"/>
+				<xs:element ref="Sk:group"/>
+				<xs:element ref="Sk:image"/>
+				<xs:element ref="Sk:int"/>
+				<xs:element ref="Sk:line"/>
+				<xs:element ref="Sk:matrix"/>
+				<xs:element ref="Sk:move"/>
+				<xs:element ref="Sk:oval"/>
+				<xs:element ref="Sk:paint"/>
+			<!--	<xs:element ref="Sk:patch"/>   -->
+				<xs:element ref="Sk:path"/>
+				<xs:element ref="Sk:point"/>
+				<xs:element ref="Sk:polygon"/>
+				<xs:element ref="Sk:polyline"/>
+				<xs:element ref="Sk:post"/>
+				<xs:element ref="Sk:random"/>
+				<xs:element ref="Sk:rect"/>
+				<xs:element ref="Sk:remove"/>
+				<xs:element ref="Sk:replace"/>
+				<xs:element ref="Sk:roundRect"/>
+				<xs:element ref="Sk:save"/>
+				<xs:element ref="Sk:snapshot"/>
+				<xs:element ref="Sk:string"/>
+				<xs:element ref="Sk:text"/>
+				<xs:element ref="Sk:textBox"/>
+				<xs:element ref="Sk:textOnPath"/>
+				<xs:element ref="Sk:textToPath"/>
+			</xs:choice>
+			<!-- @attribute animator The description of how the element is changed over time. -->
+			<xs:attribute name="animator" type="Sk:Animate"/>
+			<!-- @attribute begin An optional offset that must elapse before the animation begins. The apply
+				begin attribute is added to any animator's begin attribute. -->
+			<xs:attribute name="begin" type="Sk:MSec" />
+			<!-- @attribute dontDraw Edits an element's attribute without drawing it; for instance,
+				to edit a clip's rectangle without drawing the rectangle, set dontDraw="true".  -->
+			<xs:attribute name="dontDraw" type="Sk:Boolean"/>
+			<!-- @attribute dynamicScope The location in the display list where animations are stored. Use 
+			dynamicScope instead of scope if a script expression with potentially different values is desired to 
+			describe the scope. -->
+			<xs:attribute name="dynamicScope" type="Sk:String"/>
+			<!-- @attribute interval The optional time interval from one animation frame to the next. -->
+			<xs:attribute name="interval" type="Sk:MSec" />
+			<!-- @attribute mode One of @pattern. @patternDescription  -->
+			<xs:attribute name="mode" type="Sk:ApplyMode"/>
+			<!-- @attribute pickup Starts the animation at the current target's attribute value. Enabling
+				'pickup' permits omitting the 'from' attribute of the animator.  -->
+			<xs:attribute name="pickup" type="Sk:Boolean"/>
+			<!-- @attribute restore If true, multiple references to the same apply statement save and
+				restore the interpolated target values.  -->
+			<xs:attribute name="restore" type="Sk:Boolean"/>
+			<!-- @attribute scope The location in the display list where animations are stored.  -->
+			<xs:attribute name="scope" type="Sk:Drawable"/>
+			<!-- @attribute step When mode="immediate" or "create", the step attribute can be read by 
+				script to determine the current animation iteration.  -->
+			<xs:attribute name="step" type="Sk:Int" />
+			<!-- @attribute steps When mode="immediate", the number of times the animation
+				is stepped. The animation iterates 'steps' times plus one.  -->
+			<xs:attribute name="steps" type="Sk:Int" />
+			<!-- @attribute time When read from script, returns the animation time. Typically used by
+				an animate element's formula attribute.  -->
+			<xs:attribute name="time" type="Sk:MSec" />
+			<!-- @attribute transition One of @pattern. @patternDescription  -->
+			<xs:attribute name="transition" type="Sk:ApplyTransition"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** array
+		Array contains an array of values of the same type. The values may be
+		numbers or strings.
+	*/ -->
+	<xs:element name="array">
+		<xs:complexType>
+			<!-- @attribute length The number of elements in the array (read only). -->
+			<xs:attribute name="length" type="Sk:Int"/>
+			<!-- @attribute values The elements in the array. -->
+			<xs:attribute name="values" type="Sk:UnknownArray"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** bitmap
+		Bitmap describes a rectangle of pixels. 
+		Use the <drawTo> element to draw to a bitmap.
+		Add the bitmap to the display list to draw from a bitmap.  
+	*/ -->
+	<xs:element name="bitmap">
+		<xs:complexType>
+			<!-- @attribute erase The color, including the alpha, the bitmap is intially set to.  -->
+			<xs:attribute name="erase" type="Sk:ARGB"/>
+			<!-- @attribute format One of @pattern. @patternDescription  -->
+			<xs:attribute name="format" type="Sk:BitmapFormat"/>
+			<!-- @attribute height The height of the bitmap in pixels.  -->
+			<xs:attribute name="height" type="Sk:Int"/>
+			<!-- @attribute rowBytes The number of byte describing each row of pixels (optional).  -->
+			<xs:attribute name="rowBytes" type="Sk:Int"/>
+			<!-- @attribute  width The height of the width in pixels. -->
+			<xs:attribute name="width" type="Sk:Int"/>
+			<!-- @attribute x The left edge of the bitmap in unit space.  -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The top edge of teh bitmap in unit space.  -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** bitmapShader
+		BitmapShader sets the paint shader to draw the bitmap as a texture. 
+	*/ -->
+	<xs:element name="bitmapShader">
+		<xs:complexType>
+			<xs:choice >
+				<xs:element ref="Sk:image" minOccurs="0" />
+				<xs:element ref="Sk:matrix" minOccurs="0" />
+			</xs:choice>
+			<!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. -->
+			<xs:attribute name="matrix" type="Sk:Matrix"/>
+			<!-- @attribute tileMode One of @pattern. @patternDescription -->
+			<xs:attribute name="tileMode" type="Sk:TileMode"/>
+			<!-- @attribute filterType The bitmap filter to employ, one of @pattern. -->
+			<xs:attribute name="filterType" type="Sk:FilterType"/>
+			<!-- @attribute image The bitmap to draw. -->
+			<xs:attribute name="image" type="Sk:BaseBitmap"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	
+	<!-- /** blur
+		Blur describes an image filter in the paint that blurs the drawn geometry.  
+	*/ -->
+	<xs:element name="blur">
+		<xs:complexType>
+			<!-- @attribute blurStyle One of @pattern. @patternDescription  -->
+			<xs:attribute name="blurStyle" type="Sk:MaskFilterBlurStyle"/>
+			<!-- @attribute radius The extent of the filter effect in unit space. If the radius is less 
+				than zero,	the blur has no effect. -->		
+			<xs:attribute name="radius" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** boolean
+		Boolean contains an boolean. The boolean element cannot be added to a display list, but can
+		by set by animations and read by any attribute definition. An boolean element may be referenced,
+		for instance, by a group's condition attribute to make an animation conditionally execute.
+	*/ -->
+	<xs:element name="boolean">
+		<xs:complexType>
+			<!-- @attribute value The contained boolean. -->
+			<xs:attribute name="value" type="Sk:Boolean"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+
+	<!-- /** bounds
+		Bounds describes a bounding box that is not drawn. Bounds is used to specify a rectangle to
+		invalidate or record whether the specified area was drawn.
+		The width and height attribute compute the rectangle's right and bottom edges when the rectangle
+		description is first seen. Animating the rectangle's left or top will not recompute the right or bottom
+		if the width or height have been specified.
+	*/ -->
+	<xs:element name="bounds">
+		<xs:complexType>
+			<!-- @attribute bottom The bottom edge of the rectangle. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute height The height of the rectangle. Setting height computes the 
+				bottom attribute from the top attribute. -->
+			<xs:attribute name="height" type="Sk:Float"/>
+			<!-- @attribute inval If set to true, union the drawn bounds to compute an inval area. -->
+			<xs:attribute name="inval" type="Sk:Boolean"/>
+			<!-- @attribute left The left edge of the rectangle. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute needsRedraw Set to true if last draw was visible. -->
+			<xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+			<!-- @attribute right The right edge of the rectangle. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute top The top edge of the rectangle. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<!-- @attribute width The width of the rectangle. -->
+			<xs:attribute name="width" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** clear
+		Clear removes all entries in the display list.  
+	*/ -->
+	<xs:element name="clear">
+		<xs:complexType>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** clip
+		Clip sets the canvas to clip drawing to an element's geometry.  
+		A clip element may contain an element or reference an element with the path or
+		rectangle attributes. To make the clip unrestricted, enclose a 'full' element.
+	*/ -->
+	<xs:element name="clip">
+		<xs:complexType>
+			<xs:choice minOccurs="0" maxOccurs="1">
+				<xs:element ref="Sk:full"/>
+				<xs:element ref="Sk:rect"/>
+				<xs:element ref="Sk:path"/>
+				<xs:element ref="Sk:polygon"/>
+				<xs:element ref="Sk:polyline"/>
+			</xs:choice>
+			<!-- @attribute path A path-derived element to clip to: either an oval,
+				a path, a polygon, a polyline, or a roundRect.  -->
+			<xs:attribute name="path" type="Sk:Path"/>
+			<!-- @attribute rect A rectangle element to clip to.  -->
+			<xs:attribute name="rect" type="Sk:Rect"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** close
+		Close connects the last point in the path's contour to the first if the contour is not already closed.  
+	*/ -->
+	<xs:element name="close">
+		<xs:complexType>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** color
+		Color describes a color in RGB space or HSV space, and its alpha (transparency).  
+	*/ -->
+	<xs:element name="color">
+		<xs:complexType>
+			<!-- @attribute alpha The alpha component, which describes transparency.
+			 Alpha ranges from 0.0 (transparent) to 1.0 (completely opaque). -->
+			<xs:attribute name="alpha" type="Sk:Float"/>
+			<!-- @attribute blue The blue component of an RGB color. Blue ranges from 0 to 255.  -->
+			<xs:attribute name="blue" type="Sk:Float"/>
+			<!-- @attribute color The complete color. The color can be specified by name,
+				by hexadecimal value, or with the rgb function.  -->
+			<xs:attribute name="color" type="Sk:ARGB"/>
+			<!-- @attribute green The green component of an RGB color. Green ranges from 0 to 255.  -->
+			<xs:attribute name="green" type="Sk:Float"/>
+			<!-- @attribute hue The hue component of an HSV color. Hue ranges from 0 to 360. -->
+			<xs:attribute name="hue" type="Sk:Float"/>
+			<!-- @attribute red The red component of an RGB color. Red ranges from 0 to 255.  -->
+			<xs:attribute name="red" type="Sk:Float"/>
+			<!-- @attribute saturation The saturation component of an HSV color. Saturation ranges from 0 to 1. -->
+			<xs:attribute name="saturation" type="Sk:Float"/>
+			<!-- @attribute value The value component of an HSV color. Value ranges from 0 to 1. -->
+			<xs:attribute name="value" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** cubicTo
+		CubicTo adds a cubic to the path, using the last point in the path as the first point of the cubic. 
+	*/ -->
+	<xs:element name="cubicTo">
+		<xs:complexType>
+			<!-- @attribute x1 The x position of the first off-curve point. -->
+			<xs:attribute name="x1" type="Sk:Float"/>
+			<!-- @attribute x2 The x position of the second off-curve point. -->
+			<xs:attribute name="x2" type="Sk:Float"/>
+			<!-- @attribute x3 The x position of the final on-curve point. -->
+			<xs:attribute name="x3" type="Sk:Float"/>
+			<!-- @attribute y1 The y position of the first off-curve point. -->
+			<xs:attribute name="y1" type="Sk:Float"/>
+			<!-- @attribute y2 The y position of the second off-curve point. -->
+			<xs:attribute name="y2" type="Sk:Float"/>
+			<!-- @attribute y3 The y position of the final on-curve point. -->
+			<xs:attribute name="y3" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** dash
+		Dash describes an array of dashes and gaps that describe how the paint strokes lines,
+		rectangles, and paths. The intervals, phase, and dashed path are all measured in the same 
+		unit space. The phase and distance between dashes is unaffected by the paint's stroke width.
+	*/ -->
+	<xs:element name="dash">
+		<xs:complexType>
+			<!-- @attribute intervals An array of floats that alternately describe the lengths of 
+			dashes and gaps. Intervals must contain an even number of entries. -->
+			<xs:attribute name="intervals" type="Sk:FloatArray"/>
+			<!-- @attribute phase Phase advances the placement of the first dash. A positive phase 
+			preceeds the first dash with a gap. A negative phase shortens the length of the first dash. -->
+			<xs:attribute name="phase" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** data
+		Data provides metadata to an event. The metadata may be an integer, a float, 
+			or a string. 
+	*/ -->
+	<xs:element name="data">
+		<xs:complexType>
+			<!-- @attribute float The float value associated with the metadata. -->
+			<xs:attribute name="float" type="Sk:Float"/>
+			<!-- @attribute initialized A read-only value set to false (unused by data). -->
+			<xs:attribute name="initialized" type="Sk:Boolean"/>
+			<!-- @attribute int The integer value associated with the metadata. -->
+			<xs:attribute name="int" type="Sk:Int"/>
+			<!-- @attribute name The name of the metadata. This is the name of the data. --> 
+			<xs:attribute name="name" type="Sk:String"/>
+			<!-- @attribute string The string value associated with the metadata. -->
+			<xs:attribute name="string" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+
+	<!-- /** discrete
+		Discrete alters the edge of the stroke randomly.  Discrete is a path effect, and only has an 
+		effect when referenced from a paint.. A <pathEffect/>
+		element with no attributes will dissable discrete. 
+	*/ -->
+	<xs:element name="discrete">
+		<xs:complexType>
+			<!-- @attribute deviation The amount of wobble in the stroke. -->
+			<xs:attribute name="deviation" type="Sk:Float"/>
+			<!-- @attribute segLength The length of wobble in the stroke. -->
+			<xs:attribute name="segLength" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** drawTo
+		DrawTo images to a bitmap. The bitmap can be added to the display list
+		to draw the composite image. 
+		DrawTo can be used as an offscreen to speed complicated animations, and
+		for bitmap effects such as pixelated zooming.  
+		DrawTo can only reference a single drawable element. Use <add>,
+		<group>, or <save> to draw multiple elements with <drawTo>.
+	*/ -->
+	<xs:element name="drawTo">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded" >
+				<xs:element ref="Sk:add"/>
+				<xs:element ref="Sk:apply"/>
+				<xs:element ref="Sk:bitmap"/>
+				<xs:element ref="Sk:bounds"/>
+		<!--		<xs:element ref="Sk3D:camera"/>    -->
+				<xs:element ref="Sk:clear"/>
+				<xs:element ref="Sk:clip"/>
+				<xs:element ref="Sk:color"/>
+				<xs:element ref="Sk:full"/>
+				<xs:element ref="Sk:group"/>
+				<xs:element ref="Sk:image"/>
+				<xs:element ref="Sk:line"/>
+				<xs:element ref="Sk:matrix"/>
+				<xs:element ref="Sk:move"/>
+				<xs:element ref="Sk:oval"/>
+				<xs:element ref="Sk:paint"/>
+			<!--	<xs:element ref="Sk:patch"/>   -->
+				<xs:element ref="Sk:path"/>
+				<xs:element ref="Sk:point"/>
+				<xs:element ref="Sk:polygon"/>
+				<xs:element ref="Sk:polyline"/>
+				<xs:element ref="Sk:rect"/>
+				<xs:element ref="Sk:remove"/>
+				<xs:element ref="Sk:replace"/>
+				<xs:element ref="Sk:roundRect"/>
+				<xs:element ref="Sk:save"/>
+				<xs:element ref="Sk:text"/>
+				<xs:element ref="Sk:textBox"/>
+				<xs:element ref="Sk:textOnPath"/>
+				<xs:element ref="Sk:textToPath"/>
+			</xs:choice>
+			<!-- @attribute drawOnce If set, the drawTo will only draw a single time. -->
+			<xs:attribute name="drawOnce" type="Sk:Boolean"/>
+			<!-- @attribute use The bitmap to draw into. -->
+			<xs:attribute name="use" type="Sk:bitmap"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** dump
+		Dump prints a list of the items in the display list and all items' 
+		children to the debug console. Dump is only available in Debug
+		builds. */ -->
+	<xs:element name="dump">	
+		<xs:complexType>
+			<!-- @attribute displayList Dumps the current display list if true. The display list is also
+			dumped if dump has no attributes. -->
+			<xs:attribute name="displayList" type="Sk:Boolean"/>
+			<!-- @attribute eventList Dumps the list of events, both enabled and disabled. -->
+			<xs:attribute name="eventList" type="Sk:Boolean"/>
+			<!-- @attribute events Outputs each event element as it is enabled. -->
+			<xs:attribute name="events" type="Sk:Boolean"/>
+			<!-- @attribute groups Outputs each group element as its condition is evaluated. -->
+			<xs:attribute name="groups" type="Sk:Boolean"/>
+			<!-- @attribute name Outputs the values associated with a single named element. -->
+			<xs:attribute name="name" type="Sk:String"/>
+			<!-- @attribute posts Outputs each post element as it is enabled. -->
+			<xs:attribute name="posts" type="Sk:Boolean"/>
+            <!-- @attribute script Evaluates the provided script -->
+            <xs:attribute name="script" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** emboss
+		PRELIMINARY [to be replaced with SkEmbossMaskFilter.h doxyfomation
+		at some point]
+		Emboss applies a mask filter to the paint that makes bias the object's color
+		towards white or black depending on the normals of the path contour, giving
+		the shape a 3D raised or depressed effect.
+		Embossing is replaced by subsequent mask filter elements, or
+		disabled a negative radius, or by an empty <mask filter> element.
+	*/ -->
+	<xs:element name="emboss">
+		<xs:complexType>
+			<!-- @attribute ambient The amount of ambient light, from 0 to 1. -->		
+			<xs:attribute name="ambient" type="Sk:Float"/>
+			<!--  @attribute direction The direction of the light source, as descibed by a 3D vector. 
+				(The vector is normalized to a unit length of 1.0.) -->		
+			<xs:attribute name="direction" type="Sk:FloatArray"/>
+			<!-- @attribute radius The extent of the filter effect in unit space. If the radius is less 
+				than zero,	the emboss has no effect. -->		
+			<xs:attribute name="radius" type="Sk:Float"/>
+			<!--  @attribute specular The expotential intensity of the light, from 0 to 1. 
+				Each increase of 0.0625 doubles the intensity. -->		
+			<xs:attribute name="specular" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** event
+		Event contains a series of actions performed each time the event's criteria are satisfied.
+		These actions may modify the display list, may enable animations which in turn modify 
+		elements' attributes, and may post other events. 
+	*/ -->
+	<xs:element name="event">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded" >
+				<xs:element ref="Sk:add"/>
+				<xs:element ref="Sk:apply"/>
+				<xs:element ref="Sk:array"/>
+				<xs:element ref="Sk:bitmap"/>
+				<xs:element ref="Sk:boolean"/>
+				<xs:element ref="Sk:bounds"/>
+		<!--		<xs:element ref="Sk3D:camera"/>    -->
+				<xs:element ref="Sk:clear"/>
+				<xs:element ref="Sk:clip"/>
+				<xs:element ref="Sk:color"/>
+				<xs:element ref="Sk:drawTo"/>
+				<xs:element ref="Sk:dump"/>
+				<xs:element ref="Sk:float"/>
+				<xs:element ref="Sk:full"/>
+				<xs:element ref="Sk:group"/>
+				<xs:element ref="Sk:hitClear"/>
+				<xs:element ref="Sk:hitTest"/>
+				<xs:element ref="Sk:image"/>
+				<xs:element ref="Sk:input"/>
+				<xs:element ref="Sk:int"/>
+				<xs:element ref="Sk:line"/>
+				<xs:element ref="Sk:matrix"/>
+				<xs:element ref="Sk:move"/>
+				<xs:element ref="Sk:movie"/>
+				<xs:element ref="Sk:oval"/>
+				<xs:element ref="Sk:paint"/>
+			<!--	<xs:element ref="Sk:patch"/>   -->
+				<xs:element ref="Sk:path"/>
+				<xs:element ref="Sk:point"/>
+				<xs:element ref="Sk:polygon"/>
+				<xs:element ref="Sk:polyline"/>
+				<xs:element ref="Sk:post"/>
+				<xs:element ref="Sk:random"/>
+				<xs:element ref="Sk:rect"/>
+				<xs:element ref="Sk:remove"/>
+				<xs:element ref="Sk:replace"/>
+				<xs:element ref="Sk:roundRect"/>
+				<xs:element ref="Sk:save"/>
+				<xs:element ref="Sk:snapshot"/>
+				<xs:element ref="Sk:string"/>
+				<xs:element ref="Sk:text"/>
+				<xs:element ref="Sk:textBox"/>
+				<xs:element ref="Sk:textOnPath"/>
+				<xs:element ref="Sk:textToPath"/>
+			</xs:choice>
+			<!-- @attribute code The key code to match to a key press event, one of @pattern. 
+				If the code is set to @pattern[0], the event is never activated. -->		
+			<xs:attribute name="code" type="Sk:EventCode"/>
+			<!-- @attribute disable If true, the event cannot be activated. By default false.. -->		
+			<xs:attribute name="disable" type="Sk:Boolean"/>
+			<!-- @attribute key The character code to match to a key down event.
+				 When read, the key that activated this event. -->		
+			<xs:attribute name="key" type="Sk:String"/>
+			<!-- @attribute keys A dash-separated continuous range of character codes to match 
+				to a key	 down event. Read the key attribute to determine the key that activated this event. -->		
+			<xs:attribute name="keys" type="Sk:String"/> <!-- single or range of keys -->
+			<!-- @attribute kind The event kind that activates this event, one of @pattern. 
+				If kind equals keyChar, either attribute key or keys is expected.
+				If kind equals keyPress, attribute code is expected.
+				If kind equals onEnd, attribute target is expected. 
+				If kind equals onLoad, the event is activated when the document containing the event
+				is loaded. The onLoad attribute cannot be activated through a post event.
+				If kind equals user, the event is activated when the posted event targets this event's ID.  -->		
+			<xs:attribute name="kind" type="Sk:EventKind"/>
+			<!-- @attribute target The element to listen to which activates this event. -->
+			<xs:attribute name="target" type="Sk:String" />
+			<!-- @attribute x For click events, the x-coordinate of the click.  -->
+			<xs:attribute name="x" type="Sk:Float" />
+			<!-- @attribute y For click events, the y-coordinate of the click.  -->
+			<xs:attribute name="y" type="Sk:Float" />
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** float
+		Float contains a signed fractional value. The float element cannot be added to a display list, 
+		but can be set by animations and read by any attribute definition.
+	*/ -->
+	<xs:element name="float">
+		<xs:complexType>
+			<!-- @attribute value The contained float. -->
+			<xs:attribute name="value" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+
+	<!-- /** fromPath
+		FromPath concatenates the parent matrix with a new matrix 
+		that maps a unit vector to a point on the given path. 
+		A fromPath element may contain a path element, or may refer to a previously
+		defined path element with the path attribute.
+	*/ -->
+	<xs:element name="fromPath">
+		<xs:complexType>
+			<xs:choice >
+				<!-- @element path The path to evaluate. -->
+				<xs:element ref="Sk:path" minOccurs="0" />
+			</xs:choice>
+			<!-- @attribute mode One of @pattern. 
+			If mode is set to normal, the matrix maps the unit vector's angle and position.
+			If mode is set to angle, the matrix maps only the unit vector's angle.
+			If mode is set to position, the matrix maps only the unit vector's position. -->
+			<xs:attribute name="mode" type="Sk:FromPathMode"/>
+			<!-- @attribute offset The distance along the path to evaluate. -->
+			<xs:attribute name="offset" type="Sk:Float"/>
+			<!-- @attribute path The path to evaluate. -->
+			<xs:attribute name="path" type="Sk:Path"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** full
+		Full paints the entire canvas to the limit of the canvas' clip.
+	*/ -->
+	<xs:element name="full">
+		<xs:complexType>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** group
+		The group element collects a series of elements into a group.  The group can be referenced
+		or defined within elements, like apply, which operate on any kind of element. Groups 
+		may contain groups. An element in a group draws identically to an element outside a group.
+	*/ -->
+	<xs:element name="group">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+				<xs:element ref="Sk:add"/>
+				<xs:element ref="Sk:apply"/>
+				<xs:element ref="Sk:array"/>
+				<xs:element ref="Sk:bitmap"/>
+				<xs:element ref="Sk:boolean"/>
+				<xs:element ref="Sk:bounds"/>
+		<!--		<xs:element ref="Sk3D:camera"/>    -->
+				<xs:element ref="Sk:clear"/>
+				<xs:element ref="Sk:clip"/>
+				<xs:element ref="Sk:drawTo"/>
+				<xs:element ref="Sk:float"/>
+				<xs:element ref="Sk:full"/>
+				<xs:element ref="Sk:group"/>
+				<xs:element ref="Sk:hitClear"/>
+				<xs:element ref="Sk:hitTest"/>
+				<xs:element ref="Sk:image"/>
+				<xs:element ref="Sk:int"/>
+				<xs:element ref="Sk:line"/>
+				<xs:element ref="Sk:matrix"/>
+				<xs:element ref="Sk:move"/>
+				<xs:element ref="Sk:oval"/>
+				<xs:element ref="Sk:paint"/>
+			<!--	<xs:element ref="Sk:patch"/>   -->
+				<xs:element ref="Sk:path"/>
+				<xs:element ref="Sk:point"/>
+				<xs:element ref="Sk:polygon"/>
+				<xs:element ref="Sk:polyline"/>
+				<xs:element ref="Sk:post"/>
+				<xs:element ref="Sk:random"/>
+				<xs:element ref="Sk:rect"/>
+				<xs:element ref="Sk:remove"/>
+				<xs:element ref="Sk:replace"/>
+				<xs:element ref="Sk:roundRect"/>
+				<xs:element ref="Sk:save"/>
+				<xs:element ref="Sk:snapshot"/>
+				<xs:element ref="Sk:string"/>
+				<xs:element ref="Sk:text"/>
+				<xs:element ref="Sk:textBox"/>
+				<xs:element ref="Sk:textOnPath"/>
+				<xs:element ref="Sk:textToPath"/>
+			</xs:choice>
+			<!-- @attribute condition If present and zero, the contained elements are ignored
+				when drawn. -->
+			<xs:attribute name="condition" type="Sk:DynamicString"/>
+			<!-- @attribute enableCondition If present and zero, the contained elements are ignored
+				when enabled. -->
+			<xs:attribute name="enableCondition" type="Sk:DynamicString"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<xs:element name="hitClear" >
+		<xs:complexType>
+			<xs:choice maxOccurs="1">
+				<xs:element ref="Sk:array"/>
+			</xs:choice>
+			<!-- @attribute targets An array of element IDs to clear their hit-tested state. -->
+			<xs:attribute name="targets" type="Sk:DisplayableArray"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<xs:element name="hitTest" >
+		<xs:complexType>
+			<xs:choice maxOccurs="2">
+				<xs:element ref="Sk:array"/>
+			</xs:choice>
+			<!-- @attribute bullets An array of element IDs to test for intersection with targets. -->
+			<xs:attribute name="bullets" type="Sk:DisplayableArray"/>
+			<!-- @attribute hits The targets the bullets hit. A read-only array of indices, one index
+				per bullet. The value of the array element is the index of the target hit, or -1 if no
+				target was hit. -->
+			<xs:attribute name="hits" type="Sk:IntArray"/>
+			<!-- @attribute targets An array of element IDs to test for intersection with bullets. -->
+			<xs:attribute name="targets" type="Sk:DisplayableArray"/>
+			<!-- @attribute value Read only; set to true if some bullet hit some target. -->
+			<xs:attribute name="value" type="Sk:Boolean"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** image
+		Image creates a reference to a JPEG, PNG or GIF. The image may be referenced
+		through the local file system, the internet, or embedded in the document in Base64
+		format. The specific image type is determined by examining the byte stream.  
+	*/ -->
+	<xs:element name="image">
+		<xs:complexType>
+			<!-- @attribute base64 The image in Base64 notation. See http://rfc.net/rfc2045.html 
+			for the base64 format. -->
+			<xs:attribute name="base64" type="Sk:Base64"/>
+			<!-- @attribute height The height of the image (read-only). -->
+			<xs:attribute name="height" type="Sk:Int"/>
+			<!-- @attribute src The URI reference, local to the contaiing document. -->
+			<xs:attribute name="src" type="Sk:String"/>
+			<!-- @attribute width The width of the image (read-only). -->
+			<xs:attribute name="width" type="Sk:Int"/>
+			<!-- @attribute x The position of the left edge of the image in local coordinates. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The position of the top edge of the image in local coordinates. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** include
+		Include adds the referenced XML to the containing document. Unlike movie, the XML
+		directives can reference the document's IDs and can define new IDs that are referenced
+		by the remainder of the document or subsequent includes.  
+	*/ -->
+	<xs:element name="include">
+		<xs:complexType>
+			<!-- @attribute src The URI reference, local to the containing document, 
+			containing the include's XML. -->
+			<xs:attribute name="src" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** input
+		Input captures the metadata passed from an event. When the metadata's name or id 
+		matches the metadata's name, the metadata's payload is copied to the corresponding
+		input attribute.
+	*/ -->
+	<xs:element name="input">
+		<xs:complexType>
+			<!-- @attribute float The floating point payload carried by the metadata. -->
+			<xs:attribute name="float" type="Sk:Float"/>
+			<!-- @attribute initialized A read-only value set to true if the input received a value 
+				from the event. -->
+			<xs:attribute name="initialized" type="Sk:Boolean"/>
+			<!-- @attribute int The signed integer payload carried by the metadata. -->
+			<xs:attribute name="int" type="Sk:Int"/>
+			<!-- @attribute name The name of the metadata containing the payload. Note that 
+				the name or id may match the payload, but that XML requires the id to be
+				uniquely defined in the document, while multiple input elements may reuse 
+				the name. -->
+			<xs:attribute name="name" type="Sk:String"/>
+			<!-- @attribute string The text payload carried by the metadata. -->
+			<xs:attribute name="string" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** int
+		Int contains an integer. The int element cannot be added to a display list, but can
+		by set by animations and read by any attribute definition. An int element may be used,
+		for instance, to index through an array element.
+	*/ -->
+	<xs:element name="int">
+		<xs:complexType>
+			<!-- @attribute value The contained integer. -->
+			<xs:attribute name="value" type="Sk:Int"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+
+	<!-- /** line
+		Line describes a line between two points. As noted below, the paint's stroke and
+		strokeAndFill attributes are ignored.
+	*/ -->
+	<xs:element name="line">
+		<xs:complexType>
+			<!-- @attribute x1 The start point's x value. -->
+			<xs:attribute name="x1" type="Sk:Float"/>
+			<!-- @attribute x2 The stop point's x value. -->
+			<xs:attribute name="x2" type="Sk:Float"/>
+			<!-- @attribute y1 The start point's y value. -->
+			<xs:attribute name="y1" type="Sk:Float"/>
+			<!-- @attribute y2 The stop point's y value. -->
+			<xs:attribute name="y2" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** lineTo
+		LineTo adds a line from the last point in a path to the specified point. 
+	*/ -->
+	<xs:element name="lineTo">
+		<xs:complexType>
+			<!-- @attribute x The final path x coordinate. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The final path y coordinate. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** linearGradient
+		LinearGradient sets the paint shader to ramp between two or more colors. 
+	*/ -->
+	<xs:element name="linearGradient">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+				<xs:element ref="Sk:color"/>
+				<xs:element ref="Sk:matrix"/>
+			</xs:choice>
+			<!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. -->
+			<xs:attribute name="matrix" type="Sk:Matrix"/>
+			<!-- @attribute tileMode One of @pattern. @patternDescription -->
+			<xs:attribute name="tileMode" type="Sk:TileMode"/>
+			<!-- @attribute offsets An optional array of values used to bias the colors. The first entry
+				in the array must be 0.0, the last must be 1.0, and intermediate values must ascend. -->
+			<xs:attribute name="offsets" type="Sk:FloatArray"/>
+			<!-- @attribute points Two points describing the start and end of the gradient. -->
+			<xs:attribute name="points" type="Sk:Point"/>	<!-- not right; should be array of 2 points -->
+			<!-- @attribute unitMapper A script that returns the mapping for [0,1] for the gradient.
+				The script can use the predefined variable 'unit' to compute the mapping. For instance,
+				"unit*unit" squares the value (while still keeping it in the range of [0,1].) The computed number
+				is pinned to from 0 to 1 after the script is executed. -->
+			<xs:attribute name="unitMapper" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** maskFilter
+		MaskFilter disables any mask filter referenced by the paint. 
+	*/ -->
+	<xs:element name="maskFilter">
+		<xs:complexType>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** matrix
+		Matrix transforms all points drawn to the canvas. The matrix may translate, scale, skew, rotate,
+		or apply perspective, or apply any combination.  
+	*/ -->
+	<xs:element name="matrix">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+			<!-- @element fromPath FromPath maps a unit vector to a position and direction on a path. -->
+				<xs:element ref="Sk:fromPath"/>
+			<!-- @element polyToPoly PolyToPoly maps a points between two polygons. -->
+				<xs:element ref="Sk:polyToPoly"/>
+			<!-- @element rectToRect RectToRect maps a points between two rectangles. -->
+				<xs:element ref="Sk:rectToRect"/>
+			<!-- @element rotate Rotate computes the matrix rotation in degrees. -->
+				<xs:element ref="Sk:rotate"/>
+			<!-- @element scale Scale stretches or shrinks horizontally, vertically, or both. -->
+				<xs:element ref="Sk:scale"/>
+			<!-- @element skew Skew slants horizontally, vertically, or both. -->
+				<xs:element ref="Sk:skew"/>
+			<!-- @element translate Translate moves horizontally, vertically, or both. -->
+				<xs:element ref="Sk:translate"/>
+			</xs:choice>
+			<!-- @attribute matrix Nine floats describing a 3x3 matrix. -->
+			<xs:attribute name="matrix" type="Sk:FloatArray"/>
+			<!-- @attribute perspectX The [0][2] element of the 3x3 matrix. -->
+			<xs:attribute name="perspectX" type="Sk:Float"/>
+			<!-- @attribute perspectY The [1][2] element of the 3x3 matrix. -->
+			<xs:attribute name="perspectY" type="Sk:Float"/>
+			<!-- @attribute rotate The angle to rotate in degrees. -->
+			<xs:attribute name="rotate" type="Sk:Float"/>
+			<!-- @attribute scale The scale to apply in both X and Y.. -->
+			<xs:attribute name="scale" type="Sk:Float"/>
+			<!-- @attribute scaleX The [0][0] element of the 3x3 matrix. -->
+			<xs:attribute name="scaleX" type="Sk:Float"/>
+			<!-- @attribute scaleY The [1][1] element of the 3x3 matrix. -->
+			<xs:attribute name="scaleY" type="Sk:Float"/>
+			<!-- @attribute skewX The [0][1] element of the 3x3 matrix. -->
+			<xs:attribute name="skewX" type="Sk:Float"/>
+			<!-- @attribute skewY The [1][0] element of the 3x3 matrix. -->
+			<xs:attribute name="skewY" type="Sk:Float"/>
+			<!-- @attribute translate A point specifying the translation in X and Y. -->
+			<xs:attribute name="translate" type="Sk:Point"/>
+			<!-- @attribute translateX The [2][0] element of the 3x3 matrix. -->
+			<xs:attribute name="translateX" type="Sk:Float"/>
+			<!-- @attribute translateY The [2][1] element of the 3x3 matrix. -->
+			<xs:attribute name="translateY" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** move
+		Move an element in the display list in front or behind other elements.  
+		If where and offset are omitted, the element is moved to the end of the display list.
+		If where is specified, the element is moved before the first occurance of where in the display list.
+		If offset and where are specified, the element is moved before where plus offset.
+		A positive offset without where moves the element to the start of the list plus offset.
+		A negative offset without where moves the element to the end of the list minus offset.
+	*/ -->
+	<xs:element name="move">
+		<xs:complexType>
+			<!-- @attribute mode Has no effect. -->
+			<xs:attribute name="mode" type="Sk:AddMode"/>
+			<!-- @attribute offset The destination position using the rules listed above. -->
+			<xs:attribute name="offset" type="Sk:Int"/>
+			<!-- @attribute use The element to move. -->
+			<xs:attribute name="use" type="Sk:Drawable"/>
+			<!-- @attribute where The ID of the first display list entry to move to. -->
+			<xs:attribute name="where" type="Sk:Drawable"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** moveTo
+		MoveTo specifies the first point in a path contour.
+	*/ -->
+	<xs:element name="moveTo">
+		<xs:complexType>
+			<!-- @attribute x The point's x coordinate. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The point's y coordinate. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** movie
+		Movie describes a display list within the current canvas and paint. Movies can contain 
+		movies. One movie cannot affect how another movie draws, but movies can communicate
+		with each other by posting events.
+	*/ -->
+	<xs:element name="movie">
+		<xs:complexType>
+			<!-- @attribute src The URI reference, local to the containing document, containing the movie's XML. -->
+			<xs:attribute name="src" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** oval
+		Oval describes a circle stretched to fit in a rectangle.
+		The width and height attribute compute the oval's right and bottom edges when the oval
+		description is first seen. Animating the oval's left or top will not recompute the right or bottom
+		if the width or height have been specified.
+	*/ -->
+	<xs:element name="oval">
+		<xs:complexType>
+			<!-- @attribute bottom The bottom edge of the oval. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute height The height of the oval. -->
+			<xs:attribute name="height" type="Sk:Float"/>
+			<!-- @attribute left The left edge of the oval. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute needsRedraw Set to true if last draw was visible. -->
+			<xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+			<!-- @attribute right The right edge of the oval. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute top The top edge of the oval. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<!-- @attribute width The width of the oval. -->
+			<xs:attribute name="width" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** paint
+		Paint uses color, flags, path effects, mask filters, shaders, and stroke effects when drawing 
+		geometries, images, and text.
+	*/ -->
+	<xs:element name="paint">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+			<!-- @element bitmapShader Sets or cancels an image to draw as the color. -->
+				<xs:element ref="Sk:bitmapShader"/>
+			<!-- @element blur Blur radially draws the shape with varying transparency. -->
+				<xs:element ref="Sk:blur"/>
+			<!-- @element color Color specifies a solid color in RGB or HSV. -->
+				<xs:element ref="Sk:color"/>
+			<!-- @element dash Dashes alternates stroking with dashes and gaps. -->
+				<xs:element ref="Sk:dash"/>
+			<!-- @element discrete Discrete wobbles the geometry randomly. -->
+				<xs:element ref="Sk:discrete"/>
+			<!-- @element emboss Emboss simulates a 3D light to show highlights and relief. -->
+				<xs:element ref="Sk:emboss"/>
+			<!-- @element linearGradient LinearGradient linearly ramps between two or more colors. -->
+				<xs:element ref="Sk:linearGradient"/>
+			<!-- @element maskFilter MaskFilter cancels a blur or emboss. -->
+				<xs:element ref="Sk:maskFilter"/>
+			<!-- @element  pathEffect PathEffect cancels a discrete or dash. -->
+				<xs:element ref="Sk:pathEffect"/>
+			<!-- @element radialGradient RadialGradient radially ramps between two or more colors. -->
+				<xs:element ref="Sk:radialGradient"/>
+			<!-- @element shader Shader cancels a linear or radial gradient. -->
+				<xs:element ref="Sk:shader"/>
+			<!-- @element typeface Typeface chooses a font out of a font family. -->
+				<xs:element ref="Sk:typeface"/>
+			<!-- @element transparentShader  TransparentShader ? [not sure what this is for] -->
+				<xs:element ref="Sk:transparentShader"/>
+			</xs:choice>
+			<!-- @attribute antiAlias AntiAlias uses gray shades to increase the definition of paths. -->
+			<xs:attribute name="antiAlias" type="Sk:Boolean"/>
+			<!-- @attribute ascent Ascent returns the height above the baseline defined by the font. -->
+			<xs:attribute name="ascent" type="Sk:Float"/>
+			<!-- @attribute color Color sets the paint to the color element with this ID. -->
+			<xs:attribute name="color" type="Sk:Color"/>
+			<!-- @attribute descent Descent returns the height below the baseline defined by thte font -->
+			<xs:attribute name="descent" type="Sk:Float"/>
+			<!-- @attribute fakeBold FakeBold enables a faked bold for text. -->
+			<xs:attribute name="fakeBold" type="Sk:Boolean"/>
+			<!-- @attribute filterType FilterType -->
+			<xs:attribute name="filterType" type="Sk:FilterType"/>
+			<!-- @attribute linearText LinearText uses the ideal path metrics at all sizes to describe text. -->
+			<xs:attribute name="linearText" type="Sk:Boolean"/>
+			<!-- @attribute maskFilter MaskFilter specifies a blur or emboss with this ID. -->
+			<xs:attribute name="maskFilter" type="Sk:MaskFilter"/>
+			<!-- @attribute measureText MeasureText(String) returns the width of the string in this paint. -->
+			<xs:attribute name="measureText" type="Sk:Float"/>
+			<!-- @attribute pathEffect PathEffect specifies a discrete or dash with this ID. -->
+			<xs:attribute name="pathEffect" type="Sk:PathEffect"/>
+			<!-- @attribute shader Shader specifies a gradient with this ID. -->
+			<xs:attribute name="shader" type="Sk:Shader"/>
+			<!-- @attribute strikeThru StrikeThru adds a line through the middle of drawn text. -->
+			<xs:attribute name="strikeThru" type="Sk:Boolean"/>
+			<!-- @attribute stroke Stroke draws the outline of geometry according to the pen attributes. 
+				If style is also present, its setting overrides stroke. -->
+			<xs:attribute name="stroke" type="Sk:Boolean"/>
+			<!-- @attribute strokeCap StrokeCap is one of @pattern. -->
+			<xs:attribute name="strokeCap" type="Sk:Cap"/>
+			<!-- @attribute strokeJoin StrokeJoin is one of @pattern. -->
+			<xs:attribute name="strokeJoin" type="Sk:Join"/>
+			<!-- @attribute strokeMiter StrokeMiter limits the pen's joins on narrow angles. -->
+			<xs:attribute name="strokeMiter" type="Sk:Float"/>
+			<!-- @attribute strokeWidth StrokeWidth specifies the width of the pen. -->
+			<xs:attribute name="strokeWidth" type="Sk:Float"/>
+			<!-- @attribute style Style fills, strokes, or strokes and fills the geometry with the paint's color. -->
+			<xs:attribute name="style" type="Sk:Style"/>
+			<!-- @attribute textAlign TextAlign is one of @pattern. -->
+			<xs:attribute name="textAlign" type="Sk:Align"/>
+			<!-- @attribute textScaleX TextScaleX condenses or exapnds the text. -->
+			<xs:attribute name="textScaleX" type="Sk:Float"/>
+			<!-- @attribute textSize TextSize specifies the point size of the text. -->
+			<xs:attribute name="textSize" type="Sk:Float"/>
+			<!-- @attribute textSkewX TextSkewX draws the text obliquely. -->
+			<xs:attribute name="textSkewX" type="Sk:Float"/>
+			<!-- @attribute textTracking TextTracking specifies the space between letters. -->
+			<xs:attribute name="textTracking" type="Sk:Float"/>
+			<!-- @attribute typeface Typeface specifies a typeface element with this ID. -->
+			<xs:attribute name="typeface" type="Sk:Typeface"/>
+			<!-- @attribute underline Underline draws a line under the baseline of the text. -->
+			<xs:attribute name="underline" type="Sk:Boolean"/>
+			<!-- @attribute xfermode Xfermode specifies a transfer mode, one of @pattern. -->
+			<xs:attribute name="xfermode" type="Sk:Xfermode"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** path
+		Path creates a geometry out of lines and curves.
+	*/ -->
+	<xs:element name="path">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+			<!-- @element addCircle Adds a circle to the path. -->
+				<xs:element ref="Sk:addCircle"/>
+			<!-- @element addOval Adds an oval to the path. -->
+				<xs:element ref="Sk:addOval"/>
+			<!-- @element addPath Adds another path to the path. -->
+				<xs:element ref="Sk:addPath"/>
+			<!-- @element addRoundRect Adds a rounded-corner rectangle  to the path. -->
+				<xs:element ref="Sk:addRoundRect"/>
+			<!-- @element close Connects the last point on the path to the first. -->
+				<xs:element ref="Sk:close"/>
+			<!-- @element cubicTo Extends the path with a cubic curve. -->
+				<xs:element ref="Sk:cubicTo"/>
+			<!-- @element lineTo Extends the path with a line. -->
+				<xs:element ref="Sk:lineTo"/>
+			<!-- @element moveTo Starts a new path contour. -->
+				<xs:element ref="Sk:moveTo"/>
+			<!-- @element quadTo Extends the path with a quadratic curve. -->
+				<xs:element ref="Sk:quadTo"/>
+			<!-- @element rCubicTo Extends the path with a cubic curve expressed with relative offsets. -->
+				<xs:element ref="Sk:rCubicTo"/>
+			<!-- @element rLineTo Extends the path with a line expressed with relative offsets. -->
+				<xs:element ref="Sk:rLineTo"/>
+			<!-- @element rMoveTo Starts a new path contour relative to the path's last point. -->
+				<xs:element ref="Sk:rMoveTo"/>
+			<!-- @element rQuadTo Extends the path with a quadratic curve expressed with relative offsets. -->
+				<xs:element ref="Sk:rQuadTo"/>
+			</xs:choice>
+			<!-- @attribute d Creates a path using SVG path notation. -->
+			<xs:attribute name="d" type="Sk:String"/>
+			<!-- @attribute fillType One of @pattern. -->
+			<xs:attribute name="fillType" type="Sk:FillType"/>
+			<!-- @attribute length Returns the length of the path. -->
+			<xs:attribute name="length" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** pathEffect
+		PathEffect cancels any current path effect within the paint, such as dashing or discrete.
+	*/ -->
+	<xs:element name="pathEffect">
+		<xs:complexType>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+		
+	<!-- /** point
+		Point describes a two dimensional point in space. The point element can be added
+		to the display list and drawn.
+	*/ -->
+	<xs:element name="point">
+		<xs:complexType>
+			<!-- @attribute x The x coordinate of the point. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The y coordinate of the point. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** polygon
+		Polygon creates a geometry out of lines. Polygon is a specialization of path; element that 
+		refers to a path can refer to a polygon also. A polygon specified through elements behaves identically
+		to a path. A polygon specified by the points attribute contains a single contour, and the contour is 
+		automatically closed.
+	*/ -->
+	<xs:element name="polygon">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+			<!-- @element close Connects the last point on the path to the first. -->
+				<xs:element ref="Sk:close"/>
+			<!-- @element addPath Adds another path to the path. -->
+				<xs:element ref="Sk:addPath"/>
+			<!-- @element lineTo Extends the path with a line. -->
+				<xs:element ref="Sk:lineTo"/>
+			<!-- @element moveTo Starts a new path contour. -->
+				<xs:element ref="Sk:moveTo"/>
+			<!-- @element rLineTo Extends the path with a line expressed with relative offsets. -->
+				<xs:element ref="Sk:rLineTo"/>
+			<!-- @element rMoveTo Starts a new path contour relative to the path's last point. -->
+				<xs:element ref="Sk:rMoveTo"/>
+			</xs:choice>
+			<!-- @attribute points An array of values that describe a sequence of points, compatible with SVG. -->
+			<xs:attribute name="points" type="Sk:FloatArray"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** polyline
+		Polyline creates a geometry out of lines. Polygon is a specialization of path; element that 
+		refers to a path can refer to a polygon also. A polygon specified through elements behaves identically
+		to a path. A polygon specified by the points attribute contains a single contour, and the contour is 
+		not automatically closed.
+	*/ -->
+	<xs:element name="polyline">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+			<!-- @element close Connects the last point on the path to the first. -->
+				<xs:element ref="Sk:close"/>
+			<!-- @element addPath Adds another path to the path. -->
+				<xs:element ref="Sk:addPath"/>
+			<!-- @element lineTo Extends the path with a line. -->
+				<xs:element ref="Sk:lineTo"/>
+			<!-- @element moveTo Starts a new path contour. -->
+				<xs:element ref="Sk:moveTo"/>
+			<!-- @element rLineTo Extends the path with a line expressed with relative offsets. -->
+				<xs:element ref="Sk:rLineTo"/>
+			<!-- @element rMoveTo Starts a new path contour relative to the path's last point. -->
+				<xs:element ref="Sk:rMoveTo"/>
+			</xs:choice>
+			<!-- @attribute points An array of values that describe a sequence of points, compatible with SVG. -->
+			<xs:attribute name="points" type="Sk:FloatArray"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** polyToPoly
+		PolyToPoly creates a matrix which maps points proportionally from one polygon to the other.
+	*/ -->
+	<xs:element name="polyToPoly">
+		<xs:complexType>
+			<xs:choice maxOccurs="2">
+				<xs:element ref="Sk:polygon"/>
+			</xs:choice>
+			<!-- @attribute source The polygon to map from.. -->
+			<xs:attribute name="source" type="Sk:polygon"/>
+			<!-- @attribute destination The polygon to map to.. -->
+			<xs:attribute name="destination" type="Sk:polygon"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** post
+		Post activates an event. The event can trigger one or more actions, and can carry a data payload.
+	*/ -->
+	<xs:element name="post">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+				<xs:element ref="Sk:data"/>
+			</xs:choice>
+			<!-- @attribute delay Time in seconds that must elapse before the target event is activated. -->
+			<xs:attribute name="delay" type="Sk:MSec"/>
+			<!-- @attribute mode One of @pattern. @patternDescription -->
+			<xs:attribute name="mode" type="Sk:EventMode"/>
+			<!-- @attribute sink The optional named EventSink to direct the event to. -->
+			<xs:attribute name="sink" type="Sk:String"/>
+			<!-- @attribute target The ID of the user event to trigger. -->
+			<xs:attribute name="target" type="Sk:String"/>
+			<!-- @attribute type The name of the external event to post. -->
+			<xs:attribute name="type" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+
+	<!-- /** quadTo
+		QuadTo adds a quadratic curve to a path.
+	*/ -->
+	<xs:element name="quadTo">
+		<xs:complexType>
+			<!-- @attribute x1 The x position of the off-curve point. -->
+			<xs:attribute name="x1" type="Sk:Float"/>
+			<!-- @attribute x2 The x position of the final point. -->
+			<xs:attribute name="x2" type="Sk:Float"/>
+			<!-- @attribute y1 The y position of the off-curve point. -->
+			<xs:attribute name="y1" type="Sk:Float"/>
+			<!-- @attribute y2 The y position of the final point. -->
+			<xs:attribute name="y2" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** rCubicTo
+		RCubicTo adds a cubic to the path, using the last point in the path as the first point of the cubic.  THe
+		added points are offsets from the last point in the path.
+	*/ -->
+	<xs:element name="rCubicTo">
+		<xs:complexType>
+			<!-- @attribute x1 The x offset of the first off-curve point. -->
+			<xs:attribute name="x1" type="Sk:Float"/>
+			<!-- @attribute x2 The x offset of the second off-curve point. -->
+			<xs:attribute name="x2" type="Sk:Float"/>
+			<!-- @attribute x3 The x offset of the final on-curve point. -->
+			<xs:attribute name="x3" type="Sk:Float"/>
+			<!-- @attribute y1 The y offset of the first off-curve point. -->
+			<xs:attribute name="y1" type="Sk:Float"/>
+			<!-- @attribute y2 The y offset of the second off-curve point. -->
+			<xs:attribute name="y2" type="Sk:Float"/>
+			<!-- @attribute y3 The y offset of the final on-curve point. -->
+			<xs:attribute name="y3" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** rLineTo
+		RLineTo adds a line from the last point in a path to the specified point. The specified
+		point is relative to the last point in the path.
+	*/ -->
+	<xs:element name="rLineTo">
+		<xs:complexType>
+			<!-- @attribute x The final path x coordinate. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The final path y coordinate. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** rMoveTo
+		RMoveTo specifies the first point in a path contour. The specified
+		point is relative to the last point in the path.
+	*/ -->
+	<xs:element name="rMoveTo">
+		<xs:complexType>
+			<!-- @attribute x The point's x coordinate. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The point's y coordinate. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+		
+	<!-- /** rQuadTo
+		RQuadTo adds a quadratic curve to a path. The quadratic 
+		points are relative to the last point in the path.
+	*/ -->
+	<xs:element name="rQuadTo">
+		<xs:complexType>
+			<!-- @attribute x1 The x position of the off-curve point. -->
+			<xs:attribute name="x1" type="Sk:Float"/>
+			<!-- @attribute x2 The x position of the final point. -->
+			<xs:attribute name="x2" type="Sk:Float"/>
+			<!-- @attribute y1 The y position of the off-curve point. -->
+			<xs:attribute name="y1" type="Sk:Float"/>
+			<!-- @attribute y2 The y position of the final point. -->
+			<xs:attribute name="y2" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+		
+	<!-- /** radialGradient
+		RadialGradient sets the paint shader to ramp between two or more colors in concentric circles. 
+	*/ -->
+	<xs:element name="radialGradient">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+				<xs:element ref="Sk:color"/>
+				<xs:element ref="Sk:matrix"/>
+			</xs:choice>
+			<!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. -->
+			<xs:attribute name="matrix" type="Sk:Matrix"/>
+			<!-- @attribute tileMode One of @pattern. @patternDescription -->
+			<xs:attribute name="tileMode" type="Sk:TileMode"/>
+			<!-- @attribute center The center point of the radial gradient. -->
+			<xs:attribute name="center" type="Sk:Point"/>
+			<!-- @attribute offsets An optional array of values used to bias the colors. The first entry
+				in the array must be 0.0, the last must be 1.0, and intermediate values must ascend. -->
+			<xs:attribute name="offsets" type="Sk:FloatArray"/>
+			<!-- @attribute radius The distance from the first color to the last color. -->
+			<xs:attribute name="radius" type="Sk:Float"/>
+			<!-- @attribute unitMapper A script that returns the mapping for [0,1] for the gradient.
+				The script can use the predefined variable 'unit' to compute the mapping. For instance,
+				"unit*unit" squares the value (while still keeping it in the range of [0,1].) The computed number
+				is pinned to from 0 to 1 after the script is executed. -->
+			<xs:attribute name="unitMapper" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** random
+		Random generates a random number, from min to max. Each time the random attribute is
+		read, a new random number is generated.
+	*/ -->
+	<xs:element name="random">
+		<xs:complexType>
+			<!-- @attribute blend The random bias from 0.0 to 1.0.
+			 0.0 biias the number towards the start and end of the range.
+			 1.0 (the default) generates a linear distribution.-->
+			<xs:attribute name="blend" type="Sk:Float"/>
+			<!-- @attribute max The largest value to generate. -->
+			<xs:attribute name="max" type="Sk:Float"/>
+			<!-- @attribute min The smallest value to generate. -->
+			<xs:attribute name="min" type="Sk:Float"/>
+			<!-- @attribute random The generated value. -->
+			<xs:attribute name="random" type="Sk:Float"/>
+			<!-- @attribute seed The random seed. Identical seeds generate the same series of 
+			numbers. -->
+			<xs:attribute name="seed" type="Sk:Int"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+
+	<!-- /** rect
+		Rect describes a bounding box.
+		The width and height attribute compute the rectangle's right and bottom edges when the rectangle
+		description is first seen. Animating the rectangle's left or top will not recompute the right or bottom
+		if the width or height have been specified.
+	*/ -->
+	<xs:element name="rect">
+		<xs:complexType>
+			<!-- @attribute bottom The bottom edge of the rectangle. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute height The height of the rectangle. Setting height computes the 
+				bottom attribute from the top attribute. -->
+			<xs:attribute name="height" type="Sk:Float"/>
+			<!-- @attribute left The left edge of the rectangle. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute needsRedraw Set to true if last draw was visible. -->
+			<xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+			<!-- @attribute right The right edge of the rectangle. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute top The top edge of the rectangle. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<!-- @attribute width The width of the rectangle. -->
+			<xs:attribute name="width" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** rectToRect
+		RectToRect adds a matrix to map one rectangle's coordinates to another.
+	*/ -->
+	<xs:element name="rectToRect">
+		<xs:complexType>
+			<xs:choice maxOccurs="2">
+				<xs:element ref="Sk:rect"/>
+			</xs:choice>
+			<!-- @attribute source The rectangle to map from. -->
+			<xs:attribute name="source" type="Sk:rect"/>
+			<!-- @attribute destination The rectangle to map to. -->
+			<xs:attribute name="destination" type="Sk:rect"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** remove
+		Remove an item from the display list.
+		If where is specified, the first occurance of where in the display list is removed.
+		If offset and where are specified, the element at where plus offset is removed.
+		A positive offset without where removes the element at the start of the list plus offset.
+		A negative offset without where removes the element at the end of the list minus offset.
+	*/ -->
+	<xs:element name="remove">
+		<xs:complexType>
+			<!-- @attribute delete If true, reverse the action of apply's attribute mode="create". 
+				(Experimental.) -->
+			<xs:attribute name="delete" type="Sk:Boolean"/>
+			<!-- @attribute offset The destination position using the rules listed above. -->
+			<xs:attribute name="offset" type="Sk:Int"/>
+			<!-- @attribute where The ID of the first display list entry to remove. -->
+			<xs:attribute name="where" type="Sk:Drawable"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** replace
+		Replace an item in the display list.
+		If where is specified, the first occurance of where in the display list is replaced by use.
+		If offset and where are specified, the element at where plus offset is replaced by use.
+		A positive offset without where replaces  the element at the start of the list plus offset.
+		A negative offset without where replaces the element at the end of the list minus offset.
+	*/ -->
+	<xs:element name="replace">
+		<xs:complexType>
+			<!-- @attribute mode Has no effect. -->
+			<xs:attribute name="mode" type="Sk:AddMode"/>
+			<!-- @attribute offset The destination position using the rules listed above. -->
+			<xs:attribute name="offset" type="Sk:Int"/>
+			<!-- @attribute use The element to be added to the display list.. -->
+			<xs:attribute name="use" type="Sk:Drawable"/>
+			<!-- @attribute where The ID of the first display list entry to remove. -->
+			<xs:attribute name="where" type="Sk:Drawable"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+		
+	<!-- /** rotate
+		Rotate creates a matrix that rotates a unit vector about a center point, and concatenated
+		with the containing matrix.
+	*/ -->
+	<xs:element name="rotate">
+		<xs:complexType>
+			<!-- @attribute center A point the rotation is centered about; by default, [0.0, 0.0]. -->
+			<xs:attribute name="center" type="Sk:Point"/>
+			<!-- @attribute degrees The rotation in degrees. -->
+			<xs:attribute name="degrees" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** roundRect
+		RoundRect creates a rectangle with rounded corners. The rounded corners are specified by
+		two axes, which describe an quarter-section of the oval which is used in each corner.
+		The width and height attribute compute the rectangle's right and bottom edges when the rectangle
+		description is first seen. Animating the rectangle's left or top will not recompute the right or bottom
+		if the width or height have been specified.
+	*/ -->
+	<xs:element name="roundRect">
+		<xs:complexType>
+			<!-- @attribute bottom The bottom edge of the rectangle. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute height The height of the rectangle. Setting height computes the 
+				bottom attribute from the top attribute. -->
+			<xs:attribute name="height" type="Sk:Float"/>
+			<!-- @attribute left The left edge of the rectangle. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute needsRedraw Set to true if last draw was visible. -->
+			<xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+			<!-- @attribute right The right edge of the rectangle. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute top The top edge of the rectangle. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<!-- @attribute rx The radius of the corners on the x axis. -->
+			<xs:attribute name="rx" type="Sk:Float"/>
+			<!-- @attribute ry The radius of the corners on the y axis. -->
+			<xs:attribute name="ry" type="Sk:Float"/>
+			<!-- @attribute width The width of the rectangle. Setting width computes the 
+				right attribute from the left attribute. -->
+			<xs:attribute name="width" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** save
+		The save element collects a series of elements into a group. The state of the paint and
+		canvas are saved, so that edits to the paint and canvas within the group are restored
+		to their original value at the end of the group. 
+		The save element can be referenced
+		or defined within elements, like apply, which operate on any kind of element. Groups 
+		may contain groups. 
+	*/ -->
+	<xs:element name="save">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded">
+				<xs:element ref="Sk:add"/>
+				<xs:element ref="Sk:apply"/>
+				<xs:element ref="Sk:array"/>
+				<xs:element ref="Sk:bitmap"/>
+				<xs:element ref="Sk:boolean"/>
+				<xs:element ref="Sk:bounds"/>
+		<!--		<xs:element ref="Sk3D:camera"/>    -->
+				<xs:element ref="Sk:clear"/>
+				<xs:element ref="Sk:clip"/>
+				<xs:element ref="Sk:color"/>
+				<xs:element ref="Sk:drawTo"/>
+				<xs:element ref="Sk:float"/>
+				<xs:element ref="Sk:full"/>
+				<xs:element ref="Sk:group"/>
+				<xs:element ref="Sk:hitClear"/>
+				<xs:element ref="Sk:hitTest"/>
+				<xs:element ref="Sk:image"/>
+				<xs:element ref="Sk:int"/>
+				<xs:element ref="Sk:line"/>
+				<xs:element ref="Sk:matrix"/>
+				<xs:element ref="Sk:move"/>
+				<xs:element ref="Sk:oval"/>
+				<xs:element ref="Sk:paint"/>
+			<!--	<xs:element ref="Sk:patch"/>   -->
+				<xs:element ref="Sk:path"/>
+				<xs:element ref="Sk:point"/>
+				<xs:element ref="Sk:polygon"/>
+				<xs:element ref="Sk:polyline"/>
+				<xs:element ref="Sk:post"/>
+				<xs:element ref="Sk:random"/>
+				<xs:element ref="Sk:rect"/>
+				<xs:element ref="Sk:remove"/>
+				<xs:element ref="Sk:replace"/>
+				<xs:element ref="Sk:roundRect"/>
+				<xs:element ref="Sk:save"/>
+				<xs:element ref="Sk:set"/>
+				<xs:element ref="Sk:snapshot"/>
+				<xs:element ref="Sk:string"/>
+				<xs:element ref="Sk:text"/>
+				<xs:element ref="Sk:textBox"/>
+				<xs:element ref="Sk:textOnPath"/>
+				<xs:element ref="Sk:textToPath"/>
+			</xs:choice>
+			<!-- @attribute condition If present and zero, the contained elements are ignored. -->
+			<xs:attribute name="condition" type="Sk:DynamicString"/>
+			<!-- @attribute enableCondition If present and zero, the contained elements are ignored
+				when enabled. -->
+			<xs:attribute name="enableCondition" type="Sk:DynamicString"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** scale
+		Scale creates a matrix that scales a unit vector about a center point, and concatenated
+		with the containing matrix.
+	*/ -->
+	<xs:element name="scale">
+		<xs:complexType>
+			<!-- @attribute center A point the scale is centered about; by default, [0.0, 0.0]. -->
+			<xs:attribute name="center" type="Sk:Point"/>
+			<!-- @attribute x The factor all x values are scaled by; by default, 1.0.  -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The factor all y values are scaled by; by default, 1.0.  -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** screenplay
+		Screenplay contains all events and elements referenced by the events.
+		A document may only contain a single screenplay element.
+	*/ -->
+	<xs:element name="screenplay">
+		<xs:complexType>
+			<xs:choice maxOccurs="unbounded" >
+				<xs:element ref="Sk:add"/>
+				<xs:element ref="Sk:apply"/>
+				<xs:element ref="Sk:array"/>
+				<xs:element ref="Sk:bitmap"/>
+				<xs:element ref="Sk:boolean"/>
+				<xs:element ref="Sk:bounds"/>
+		<!--		<xs:element ref="Sk3D:camera"/>    -->
+				<xs:element ref="Sk:clear"/>
+				<xs:element ref="Sk:clip"/>
+				<xs:element ref="Sk:color"/>
+				<xs:element ref="Sk:drawTo"/>
+				<xs:element ref="Sk:event"/>
+				<xs:element ref="Sk:float"/>
+				<xs:element ref="Sk:full"/>
+				<xs:element ref="Sk:group"/>
+				<xs:element ref="Sk:hitClear"/>
+				<xs:element ref="Sk:hitTest"/>
+				<xs:element ref="Sk:image"/>
+				<xs:element ref="Sk:include"/>
+				<xs:element ref="Sk:int"/>
+				<xs:element ref="Sk:line"/>
+				<xs:element ref="Sk:matrix"/>
+				<xs:element ref="Sk:move"/>
+				<xs:element ref="Sk:movie"/>
+				<xs:element ref="Sk:oval"/>
+				<xs:element ref="Sk:paint"/>
+			<!--	<xs:element ref="Sk:patch"/>   -->
+				<xs:element ref="Sk:path"/>
+				<xs:element ref="Sk:point"/>
+				<xs:element ref="Sk:polygon"/>
+				<xs:element ref="Sk:polyline"/>
+				<xs:element ref="Sk:post"/>
+				<xs:element ref="Sk:random"/>
+				<xs:element ref="Sk:rect"/>
+				<xs:element ref="Sk:remove"/>
+				<xs:element ref="Sk:replace"/>
+				<xs:element ref="Sk:roundRect"/>
+				<xs:element ref="Sk:save"/>
+				<xs:element ref="Sk:set"/>
+				<xs:element ref="Sk:snapshot"/>
+				<xs:element ref="Sk:string"/>
+				<xs:element ref="Sk:text"/>
+				<xs:element ref="Sk:textBox"/>
+				<xs:element ref="Sk:textOnPath"/>
+				<xs:element ref="Sk:textToPath"/>
+			</xs:choice>
+			<!-- @attribute time The time of the draw (readable from script; not part of the document XML) -->
+			<xs:attribute name="time" type="Sk:MSec"/>
+	</xs:complexType>
+	</xs:element>
+	
+	<!-- /** set
+		Set animates the target element's attribute directly to the specified value.
+	*/ -->
+	<xs:element name="set">
+		<xs:complexType>
+			<!-- @attribute begin An optional offset that must elapse before the animation begins. The apply
+				begin attribute is added to any animator's begin attribute. -->
+			<xs:attribute name="begin" type="Sk:MSec"/>
+			<!-- @attribute dur The duration of the animation in milliseconds. -->
+			<xs:attribute name="dur" type="Sk:MSec"/>
+			<!-- @attribute dynamic If true, restart the animation if any of the simple values the 
+			 'lval' or 'to' attributes reference are changed. Simple values are contained by the array, boolean, float, int, 
+			 and string elements. -->
+			<!-- @attribute dynamic [Depreciated.] -->
+			<xs:attribute name="dynamic" type="Sk:Boolean" />
+			<!-- @attribute field The attribute to animate. -->
+			<xs:attribute name="field" type="Sk:String"/>
+			<!-- @attribute formula A script to execute over time to compute the field's value. Typically,
+				the fomula is a script expression which includes a reference to the time attribute of the 
+				containing apply	element.  -->
+			<xs:attribute name="formula" type="Sk:DynamicString"/>
+			<!-- @attribute lval An expression evaluating to the attribute to animate.
+				If present, lval overrides 'field'. The expression is typically an array element,
+				e.g. lval="x[y]" . -->
+			<xs:attribute name="lval" type="Sk:DynamicString"/>
+			<!-- @attribute reset  If true, the computed value is the initial value after the 
+				animation is complete. If false, or by default, the computed value is the final value 
+				after the animation is complete. -->
+			<xs:attribute name="reset" type="Sk:Boolean"/>
+			<!-- @attribute step When apply's attribute mode="immediate" or "create", the step attribute can be read by 
+				script to determine the current animation iteration.  -->
+			<xs:attribute name="step" type="Sk:Int" />
+			<!-- @attribute target The element to animate. By default, the element contained by the apply
+				or referenced by the apply's scope attribute is the animate target. -->
+			<xs:attribute name="target" type="Sk:DynamicString"/>
+			<!-- @attribute to The ending value (requires a 'from' attribute) -->
+			<xs:attribute name="to" type="Sk:DynamicString"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** skew
+		Skew creates a matrix that skews a unit vector about a center point, and concatenated
+		with the containing matrix.
+	*/ -->
+	<xs:element name="skew">
+		<xs:complexType>
+			<!-- @attribute center A point the skew is centered about; by default, [0.0, 0.0]. -->
+			<xs:attribute name="center" type="Sk:Point"/>
+			<!-- @attribute x The factor all x values are skewed by; by default, 0.0.  -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The factor all y values are skewed by; by default, 0.0.  -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** snapshot
+		Snapshot creates an image file containing the display list.
+	*/ -->
+	<xs:element name="snapshot">
+		<xs:complexType>
+			<!-- @attribute filename The name of the file to generate. -->
+			<xs:attribute name="filename" type="Sk:String"/>
+			<!-- @attribute quality The quality of the image, from 0 to 100. -->
+			<xs:attribute name="quality" type="Sk:Float"/>
+			<!-- @attribute sequence Set to true to number the filenames sequentially. -->
+			<xs:attribute name="sequence" type="Sk:Boolean"/>
+			<!-- @attribute type One of @pattern. The type of encoding to use. -->
+			<xs:attribute name="type" type="Sk:BitmapEncoding"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** string
+		String contains an array of characters.
+	*/ -->
+	<xs:element name="string" >
+		<xs:complexType>
+			<!-- @attribute length The number of characters in the string (read only). -->
+			<xs:attribute name="length" type="Sk:Int"/>
+			<!-- @attribute slice An ECMAScript compatible function that returns part of the string. -->
+			<xs:attribute name="slice" type="Sk:String"/>
+			<!-- @attribute value The string itself. -->
+			<xs:attribute name="value" type="Sk:String"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** text
+		A drawable string with a position.
+	*/ -->
+	<xs:element name="text">
+		<xs:complexType>
+			<!-- @attribute length The number of characters in the string (read only). -->
+			<xs:attribute name="length" type="Sk:Int"/>
+			<!-- @attribute text The string itself. -->
+			<xs:attribute name="text" type="Sk:String"/>
+			<!-- @attribute x The x coordinate of the string. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The y coordinate of the string. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** textBox
+		A drawable string fit into a box.
+	*/ -->
+	<xs:element name="textBox" >
+		<xs:complexType>
+			<!-- @attribute bottom The bottom of the box. -->
+			<xs:attribute name="bottom" type="Sk:Float"/>
+			<!-- @attribute height The height of the box, computed from top and bottom. -->
+			<xs:attribute name="height" type="Sk:Float"/>
+			<!-- @attribute left The left side of the box. -->
+			<xs:attribute name="left" type="Sk:Float"/>
+			<!-- @attribute mode One of @pattern. -->
+			<xs:attribute name="mode" type="Sk:TextBoxMode"/>
+			<!-- @attribute needsRedraw Set to true if last draw was visible. -->
+			<xs:attribute name="needsRedraw" type="Sk:Boolean"/>
+			<!-- @attribute right The right side of the box. -->
+			<xs:attribute name="right" type="Sk:Float"/>
+			<!-- @attribute spacingAdd The extra spacing between lines. -->
+			<xs:attribute name="spacingAdd" type="Sk:Float"/>
+			<!-- @attribute spacingAlign One of @pattern. -->
+			<xs:attribute name="spacingAlign" type="Sk:TextBoxAlign"/>
+			<!-- @attribute spacingMul The line spacing scaled by the text height. -->
+			<xs:attribute name="spacingMul" type="Sk:Float"/>
+			<!-- @attribute text The text to fit to the box. -->
+			<xs:attribute name="text" type="Sk:String"/>
+			<!-- @attribute top The top of the box. -->
+			<xs:attribute name="top" type="Sk:Float"/>
+			<!-- @attribute width The width of the box, computed from left and right. -->
+			<xs:attribute name="width" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** textOnPath
+		TextOnPath specifies the baseline for a string of text with a path.
+	*/ -->
+	<xs:element name="textOnPath">
+		<xs:complexType>
+			<xs:choice >
+				<xs:element ref="Sk:text" minOccurs="0" />
+				<xs:element ref="Sk:path" minOccurs="0" />
+			</xs:choice>
+			<!-- @attribute offset The distance along the path to place the first text character. -->
+			<xs:attribute name="offset" type="Sk:Float"/>
+			<!-- @attribute path The baseline of the text. -->
+			<xs:attribute name="path" type="Sk:Path"/>
+			<!-- @attribute text The text to place along the path. -->
+			<xs:attribute name="text" type="Sk:Text"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** textToPath
+		TextToPath sets the path to the contours described by the text's glyphs, using the current paint.
+	*/ -->
+	<xs:element name="textToPath">
+		<xs:complexType>
+			<xs:choice >
+				<xs:element ref="Sk:text" minOccurs="0" />
+				<xs:element ref="Sk:paint" minOccurs="0" />
+				<xs:element ref="Sk:path" minOccurs="0" />
+			</xs:choice>
+			<!-- @attribute paint The paint selects the text font, size and other text properties. -->
+			<xs:attribute name="paint" type="Sk:Paint"/>
+			<!-- @attribute path The reference to the path element where the text as path is stored. -->
+			<xs:attribute name="path" type="Sk:Path"/>
+			<!-- @attribute text The reference to the text element to turn into a path. -->
+			<xs:attribute name="text" type="Sk:Text"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** translate
+		Translate concatenates a translation-only matrix onto the current matrix.
+	*/ -->
+	<xs:element name="translate">
+		<xs:complexType>
+			<!-- @attribute x The translation in x. -->
+			<xs:attribute name="x" type="Sk:Float"/>
+			<!-- @attribute y The translation in y. -->
+			<xs:attribute name="y" type="Sk:Float"/>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** transparentShader
+		TransparentShader uses the background for its paint.  Works well with emboss.
+	*/ -->
+	<xs:element name="transparentShader">
+		<xs:complexType>
+			<xs:attribute name="id" type="xs:ID"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<!-- /** typeface
+		Typeface describes the text font.
+	*/ -->
+	<xs:element name="typeface">
+		<xs:complexType>
+			<!-- @attribute fontName The name of the font. -->
+			<xs:attribute name="fontName" type="Sk:String"/>
+		</xs:complexType>
+	</xs:element>
+	
+</xs:schema>
+
diff --git a/legacy/src/animator/SkAnimateSchema.xsx b/legacy/src/animator/SkAnimateSchema.xsx
new file mode 100644
index 0000000..ceb7d89
--- /dev/null
+++ b/legacy/src/animator/SkAnimateSchema.xsx
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!--This file is auto-generated by the XML Schema Designer. It holds layout information for components on the designer surface.-->

+<XSDDesignerLayout />
diff --git a/legacy/src/animator/SkAnimateSet.cpp b/legacy/src/animator/SkAnimateSet.cpp
new file mode 100644
index 0000000..d0b4dfc
--- /dev/null
+++ b/legacy/src/animator/SkAnimateSet.cpp
@@ -0,0 +1,91 @@
+
+/*
+ * 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 "SkAnimateSet.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateProperties.h"
+#include "SkParse.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSet::fInfo[] = {
+    SK_MEMBER(begin, MSec),
+    SK_MEMBER(dur, MSec),
+    SK_MEMBER_PROPERTY(dynamic, Boolean),
+    SK_MEMBER(field, String),
+//  SK_MEMBER(formula, DynamicString),
+    SK_MEMBER(lval, DynamicString),
+//  SK_MEMBER_PROPERTY(reset, Boolean),
+    SK_MEMBER_PROPERTY(step, Int),
+    SK_MEMBER(target, DynamicString),
+    SK_MEMBER(to, DynamicString)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSet);
+
+SkSet::SkSet() {
+    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");
+
+}
+#endif
+
+void SkSet::refresh(SkAnimateMaker& maker) {
+    fFieldInfo->setValue(maker, &fValues, 0, fFieldInfo->fCount, NULL, 
+        fFieldInfo->getType(), to);
+}
+
+void SkSet::onEndElement(SkAnimateMaker& maker) {
+    if (resolveCommon(maker) == false)
+        return;
+    if (fFieldInfo == NULL) {
+        maker.setErrorCode(SkDisplayXMLParserError::kFieldNotInTarget);
+        return;
+    }
+    fReset = dur != 1;
+    SkDisplayTypes outType = fFieldInfo->getType();
+    int comps = outType == SkType_String || outType == SkType_DynamicString ? 1 :
+        fFieldInfo->getSize((const SkDisplayable*) fTarget) / sizeof(int);
+    if (fValues.getType() == SkType_Unknown) {
+        fValues.setType(outType);
+        fValues.setCount(comps);
+        if (outType == SkType_String || outType == SkType_DynamicString)
+            fValues[0].fString = SkNEW(SkString);
+        else
+            memset(fValues.begin(), 0, fValues.count() * sizeof(fValues.begin()[0]));
+    } else {
+        SkASSERT(fValues.getType() == outType);
+        if (fFieldInfo->fType == SkType_Array)
+            comps = fValues.count();
+        else {
+            SkASSERT(fValues.count() == comps);
+        }
+    }
+    if (formula.size() > 0) {
+        comps = 1;
+        outType = SkType_MSec;
+    }
+    fFieldInfo->setValue(maker, &fValues, fFieldOffset, comps, this, outType, formula.size() > 0 ? formula : to);
+    fComponents = fValues.count();
+}
diff --git a/legacy/src/animator/SkAnimateSet.h b/legacy/src/animator/SkAnimateSet.h
new file mode 100644
index 0000000..c735a08
--- /dev/null
+++ b/legacy/src/animator/SkAnimateSet.h
@@ -0,0 +1,28 @@
+
+/*
+ * 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 SkAnimateSet_DEFINED
+#define SkAnimateSet_DEFINED
+
+#include "SkAnimate.h"
+
+class SkSet : public SkAnimate {
+    DECLARE_MEMBER_INFO(Set);
+    SkSet();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void refresh(SkAnimateMaker& );
+private:
+    typedef SkAnimate INHERITED;
+};
+
+#endif // SkAnimateSet_DEFINED
+
diff --git a/legacy/src/animator/SkAnimator.cpp b/legacy/src/animator/SkAnimator.cpp
new file mode 100644
index 0000000..c1fba65
--- /dev/null
+++ b/legacy/src/animator/SkAnimator.cpp
@@ -0,0 +1,705 @@
+
+/*
+ * 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 "SkAnimator.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayMovie.h"
+#include "SkDisplayTypes.h"
+#include "SkDisplayXMLParser.h"
+#include "SkStream.h"
+#include "SkScript.h"
+#include "SkScript2.h" //   compiled script experiment
+#include "SkSystemEventTypes.h"
+#include "SkTypedArray.h"
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkDrawExtraPathEffect.h"
+#endif
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+
+#if defined SK_BUILD_FOR_WIN32 && defined SK_DEBUG
+    #define _static
+    extern const char gMathPrimerText[];
+    extern const char gMathPrimerBinary[];
+#else
+    #define _static static
+#endif
+
+_static const char gMathPrimerText[] = 
+"<screenplay>"
+    "<Math id=\"Math\"/>"
+    "<Number id=\"Number\"/>"
+"</screenplay>";
+
+#define gMathPrimer gMathPrimerText
+
+SkAnimator::SkAnimator() : fMaker(NULL) {
+    initialize();
+}
+
+SkAnimator::~SkAnimator() {
+    SkDELETE(fMaker);
+}
+
+void SkAnimator::addExtras(SkExtras* extras) {
+    *fMaker->fExtras.append() = extras;
+}
+
+bool SkAnimator::appendStream(SkStream* stream) {
+    return decodeStream(stream);
+}
+
+bool SkAnimator::decodeMemory(const void* buffer, size_t size)
+{
+    fMaker->fFileName.reset();
+    SkDisplayXMLParser parser(*fMaker);
+    return parser.parse((const char*)buffer, size);
+}
+
+bool SkAnimator::decodeStream(SkStream* stream)
+{
+    SkDisplayXMLParser parser(*fMaker);
+    bool result = parser.parse(*stream);
+    fMaker->setErrorString();
+    return result;
+}
+
+bool SkAnimator::decodeDOM(const SkDOM& dom, const SkDOMNode* node)
+{
+    fMaker->fFileName.reset();
+    SkDisplayXMLParser parser(*fMaker);
+    return parser.parse(dom, node);
+}
+
+bool SkAnimator::decodeURI(const char uri[]) {
+//  SkDebugf("animator decode %s\n", uri);
+
+//    SkStream* stream = SkStream::GetURIStream(fMaker->fPrefix.c_str(), uri);
+    SkStream* stream = new SkFILEStream(uri);
+
+    SkAutoTDelete<SkStream> autoDel(stream);
+    setURIBase(uri);
+    return decodeStream(stream);
+}
+
+bool SkAnimator::doCharEvent(SkUnichar code) {
+    if (code == 0)
+        return false;
+    struct SkEventState state;
+    state.fCode = code;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyChar, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doClickEvent(int clickState, SkScalar x, SkScalar y) {
+    SkASSERT(clickState >= 0 && clickState <= 2);
+    struct SkEventState state;
+    state.fX = x;
+    state.fY = y;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, 
+        clickState == 0 ? SkDisplayEvent::kMouseDown : 
+        clickState == 1 ? SkDisplayEvent::kMouseDrag : 
+        SkDisplayEvent::kMouseUp, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doKeyEvent(SkKey code) {
+    if (code == 0)
+        return false;
+    struct SkEventState state;
+    state.fCode = code;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPress, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doKeyUpEvent(SkKey code) {
+    if (code == 0)
+        return false;
+    struct SkEventState state;
+    state.fCode = code;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPressUp, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doUserEvent(const SkEvent& evt) {
+    fMaker->fEnableTime = fMaker->getAppTime();
+    return onEvent(evt);
+}
+
+SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkPaint* paint, SkMSec time) {
+    if (paint == NULL)
+        return draw(canvas, time);
+    fMaker->fScreenplay.time = time;
+    fMaker->fCanvas = canvas;
+    fMaker->fPaint = paint;
+    fMaker->fDisplayList.fHasUnion = false;
+    int result = fMaker->fDisplayList.draw(*fMaker, time);
+    if (result)
+        result += fMaker->fDisplayList.fHasUnion;
+    return (DifferenceType) result;
+}
+
+SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkMSec time) {
+    SkPaint paint;
+    return draw(canvas, &paint, time);
+}
+    
+#ifdef SK_DEBUG
+void SkAnimator::eventDone(const SkEvent& ) {
+}
+#endif
+
+bool SkAnimator::findClickEvent(SkScalar x, SkScalar y) {
+    struct SkEventState state;
+    state.fDisable = true;
+    state.fX = x;
+    state.fY = y;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kMouseDown, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+const SkAnimator* SkAnimator::getAnimator(const SkDisplayable* displayable) const {
+    if (displayable->getType() != SkType_Movie)
+        return NULL;
+    const SkDisplayMovie* movie = (const SkDisplayMovie*) displayable;
+    return movie->getAnimator();
+}
+
+const SkDisplayable* SkAnimator::getElement(const char* id) {
+    SkDisplayable* element;
+    if (fMaker->find(id, &element) == false)
+        return NULL;
+    return (const SkDisplayable*) element;
+}
+
+SkElementType SkAnimator::getElementType(const SkDisplayable* ae) {
+    SkDisplayable* element = (SkDisplayable*) ae;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(fMaker, element->getType(), NULL);
+    return (SkElementType) SkDisplayType::Find(fMaker, info);
+}
+
+SkElementType SkAnimator::getElementType(const char* id) {
+    const SkDisplayable* element = getElement(id);
+    return getElementType(element);
+}
+
+const SkMemberInfo* SkAnimator::getField(const SkDisplayable* ae, const char* field) {
+    SkDisplayable* element = (SkDisplayable*) ae;
+    const SkMemberInfo* info = element->getMember(field);
+    return (const SkMemberInfo*) info;
+}
+
+const SkMemberInfo* SkAnimator::getField(const char* elementID, const char* field) {
+    const SkDisplayable* element = getElement(elementID);
+    return getField(element, field);
+}
+
+SkFieldType SkAnimator::getFieldType(const SkMemberInfo* ai) {
+    const SkMemberInfo* info = (const SkMemberInfo*) ai;
+    return (SkFieldType) info->getType();
+}
+
+SkFieldType SkAnimator::getFieldType(const char* id, const char* fieldID) {
+    const SkMemberInfo* field = getField(id, fieldID);
+    return getFieldType(field);
+}
+
+ static bool getArrayCommon(const SkDisplayable* ae, const SkMemberInfo* ai,
+     int index, SkOperand* operand, SkDisplayTypes type) {
+    const SkDisplayable* element = (const SkDisplayable*) ae;
+    const SkMemberInfo* info = (const SkMemberInfo*) ai;
+    SkASSERT(info->fType == SkType_Array);
+    return info->getArrayValue(element, index, operand);
+}
+
+int32_t SkAnimator::getArrayInt(const SkDisplayable* ae, 
+        const SkMemberInfo* ai, int index) {
+    SkOperand operand;
+    bool result = getArrayCommon(ae, ai, index, &operand, SkType_Int);
+    return result ? operand.fS32 : SK_NaN32;
+}
+
+int32_t SkAnimator::getArrayInt(const char* id, const char* fieldID, int index) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_NaN32;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_NaN32;
+    return getArrayInt(element, field, index);
+}
+
+SkScalar SkAnimator::getArrayScalar(const SkDisplayable* ae, 
+        const SkMemberInfo* ai, int index) {
+    SkOperand operand;
+    bool result = getArrayCommon(ae, ai, index, &operand, SkType_Float);
+    return result ? operand.fScalar : SK_ScalarNaN;
+}
+
+SkScalar SkAnimator::getArrayScalar(const char* id, const char* fieldID, int index) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_ScalarNaN;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_ScalarNaN;
+    return getArrayScalar(element, field, index);
+}
+
+const char* SkAnimator::getArrayString(const SkDisplayable* ae, 
+        const SkMemberInfo* ai, int index) {
+    SkOperand operand;
+    bool result = getArrayCommon(ae, ai, index, &operand, SkType_String);
+    return result ? operand.fString->c_str() : NULL;
+}
+
+const char* SkAnimator::getArrayString(const char* id, const char* fieldID, int index) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return NULL;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return NULL;
+    return getArrayString(element, field, index);
+}
+
+SkMSec SkAnimator::getInterval() {
+    return fMaker->fMinimumInterval == (SkMSec) -1 ? 0 : fMaker->fMinimumInterval;
+}
+
+void SkAnimator::getInvalBounds(SkRect* inval) {
+    if (fMaker->fDisplayList.fHasUnion) {
+        inval->fLeft = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fLeft);
+        inval->fTop = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fTop);
+        inval->fRight = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fRight);
+        inval->fBottom = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fBottom);
+    } else {
+        inval->fLeft = inval->fTop = -SK_ScalarMax;
+        inval->fRight = inval->fBottom = SK_ScalarMax;
+    }
+}
+
+const SkXMLParserError* SkAnimator::getParserError() {
+    return &fMaker->fError;
+}
+
+const char* SkAnimator::getParserErrorString() {
+    if (fMaker->fErrorString.size() == 0 && fMaker->fError.hasError())
+        fMaker->setErrorString();
+    return fMaker->fErrorString.c_str();
+}
+
+int32_t SkAnimator::getInt(const SkDisplayable* element, const SkMemberInfo* info) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        if (info->getType() == SkType_Int) {
+            info->getValue(element, &operand, 1);
+            return operand.fS32;
+        }
+        return SK_NaN32;
+    }
+    SkScriptValue scriptValue;
+    bool success = element->getProperty(info->propertyIndex(), &scriptValue);
+    if (success && scriptValue.fType == SkType_Int)
+        return scriptValue.fOperand.fS32;
+    return SK_NaN32;
+}
+
+int32_t SkAnimator::getInt(const char* id, const char* fieldID) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_NaN32;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_NaN32;
+    return getInt(element, field);
+}
+
+SkScalar SkAnimator::getScalar(const SkDisplayable* element, const SkMemberInfo* info) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        if (info->getType() == SkType_Float) {
+            info->getValue(element, &operand, 1);
+            return operand.fScalar;
+        }
+        return SK_ScalarNaN;
+    }
+    SkScriptValue scriptValue;
+    bool success = element->getProperty(info->propertyIndex(), &scriptValue);
+    if (success && scriptValue.fType == SkType_Float)
+        return scriptValue.fOperand.fScalar;
+    return SK_ScalarNaN;
+}
+
+SkScalar SkAnimator::getScalar(const char* id, const char* fieldID) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_ScalarNaN;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_ScalarNaN;
+    return getScalar(element, field);
+}
+
+const char* SkAnimator::getString(const SkDisplayable* ae, 
+        const SkMemberInfo* ai) {
+    const SkDisplayable* element = (const SkDisplayable*) ae;
+    const SkMemberInfo* info = (const SkMemberInfo*) ai;
+    SkString* temp;
+    info->getString(element, &temp);
+    return temp->c_str();
+}
+
+const char* SkAnimator::getString(const char* id, const char* fieldID) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return NULL;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return NULL;
+    return getString(element, field);
+}
+
+const char* SkAnimator::getURIBase() {
+    return fMaker->fPrefix.c_str();
+}
+
+void SkAnimator::initialize() {
+    SkDELETE(fMaker);
+    fMaker = SkNEW_ARGS(SkAnimateMaker, (this, NULL, NULL));
+    decodeMemory(gMathPrimer, sizeof(gMathPrimer)-1);
+#ifdef SK_BUILD_FOR_ANDROID
+    InitializeSkExtraPathEffects(this);
+#endif
+}
+
+
+#ifdef SK_DEBUG
+bool SkAnimator::isTrackingEvents() {
+    return false;
+}
+#endif
+
+bool SkAnimator::onEvent(const SkEvent& evt) {
+#ifdef SK_DEBUG
+    SkAnimator* root = fMaker->getRoot();
+    if (root == NULL)
+        root = this;
+    if (root->isTrackingEvents())
+        root->eventDone(evt);
+#endif
+    if (evt.isType(SK_EventType_OnEnd)) {
+        SkEventState eventState;
+        bool success = evt.findPtr("anim", (void**) &eventState.fDisplayable);
+        SkASSERT(success);
+        success = evt.findS32("time", (int32_t*) &fMaker->fEnableTime);
+        SkASSERT(success);
+        fMaker->fAdjustedStart = fMaker->getAppTime() - fMaker->fEnableTime;
+        fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kOnEnd, &eventState);
+        fMaker->fAdjustedStart = 0;
+        goto inval;
+    }
+    if (evt.isType(SK_EventType_Delay)) {
+        fMaker->doDelayedEvent();
+        goto inval;
+    }
+    {
+        const char* id = evt.findString("id");
+        if (id == NULL)
+            return false;
+        SkDisplayable** firstMovie = fMaker->fMovies.begin();
+        SkDisplayable** endMovie = fMaker->fMovies.end();
+        for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) {
+            SkDisplayMovie* movie = (SkDisplayMovie*) *ptr;
+            movie->doEvent(evt);
+        }
+        {
+            SkDisplayable* event;
+            if (fMaker->find(id, &event) == false)
+                return false;
+    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            SkString debugOut;
+            SkMSec realTime = fMaker->getAppTime();
+            debugOut.appendS32(realTime - fMaker->fDebugTimeBase);
+            debugOut.append(" onEvent id=");
+            debugOut.append(id);
+    #endif
+            SkMSec time = evt.getFast32();
+            if (time != 0) {
+                SkMSec app  = fMaker->getAppTime();
+                fMaker->setEnableTime(app, time);
+    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+                debugOut.append(" time=");
+                debugOut.appendS32(time - fMaker->fDebugTimeBase);
+                debugOut.append(" adjust=");
+                debugOut.appendS32(fMaker->fAdjustedStart);
+    #endif
+            }
+    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            SkDebugf("%s\n", debugOut.c_str());
+    #endif
+            SkASSERT(event->isEvent());
+            SkDisplayEvent* displayEvent = (SkDisplayEvent*) event;
+            displayEvent->populateInput(*fMaker, evt);
+            displayEvent->enableEvent(*fMaker);
+        }
+    }
+inval:
+    fMaker->notifyInval();
+    return true;
+}
+
+void SkAnimator::onEventPost(SkEvent* evt, SkEventSinkID sinkID)
+{
+#ifdef SK_DEBUG
+    SkAnimator* root = fMaker->getRoot();
+    if (root) {
+        root->onEventPost(evt, sinkID);
+        return;
+    }
+#else
+    SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID);
+#endif
+    evt->setTargetID(sinkID)->post();
+}
+
+void SkAnimator::onEventPostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time)
+{
+#ifdef SK_DEBUG
+    SkAnimator* root = fMaker->getRoot();
+    if (root) {
+        root->onEventPostTime(evt, sinkID, time);
+        return;
+    }
+#else
+    SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID);
+#endif
+    evt->setTargetID(sinkID)->postTime(time);
+}
+
+void SkAnimator::reset() {
+    fMaker->fDisplayList.reset();
+}
+
+SkEventSinkID SkAnimator::getHostEventSinkID() const {
+    return fMaker->fHostEventSinkID; 
+}
+
+void SkAnimator::setHostEventSinkID(SkEventSinkID target) {
+    fMaker->fHostEventSinkID = target; 
+}
+
+void SkAnimator::onSetHostHandler(Handler ) {
+}
+
+void SkAnimator::setJavaOwner(Handler ) {
+}
+
+bool SkAnimator::setArrayString(const char* id, const char* fieldID, const char** array, int num)
+{
+    SkTypedArray tArray(SkType_String);
+    tArray.setCount(num);
+    for (int i = 0; i < num; i++) {
+        SkOperand op;
+        op.fString = new SkString(array[i]);
+        tArray[i] = op;
+    }
+    return setArray(id, fieldID, tArray);
+}
+bool SkAnimator::setArrayInt(const char* id, const char* fieldID, const int* array, int num)
+{
+    SkTypedArray tArray(SkType_Int);
+    tArray.setCount(num);
+    for (int i = 0; i < num; i++) {
+        SkOperand op;   
+        op.fS32 = array[i];
+        tArray[i] = op;
+    }
+    return setArray(id, fieldID, tArray);
+}
+
+bool SkAnimator::setArray(SkDisplayable* element, const SkMemberInfo* info, SkTypedArray array) {
+    if (info->fType != SkType_Array)
+        return false;   //the field is not an array
+    //i think we can handle the case where the displayable itself is an array differently from the
+    //case where it has an array - for one thing, if it is an array, i think we can change its type
+    //if it's not, we cannot
+    SkDisplayTypes type = element->getType();
+    if (type == SkType_Array) {
+        SkDisplayArray* dispArray = (SkDisplayArray*) element;
+        dispArray->values = array;  
+        return true;
+    }
+    else
+        return false;   //currently i don't care about this case
+}
+
+bool SkAnimator::setArray(const char* id, const char* fieldID, SkTypedArray array) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    //should I go ahead and change all 'NULL's to 'NULL'?
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setArray(element, field, array);
+}
+
+bool SkAnimator::setInt(SkDisplayable* element, const SkMemberInfo* info, int32_t s32) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        operand.fS32 = s32;
+        SkASSERT(info->getType() == SkType_Int);
+        info->setValue(element, &operand, 1);
+    } else {
+        SkScriptValue scriptValue;
+        scriptValue.fType = SkType_Int;
+        scriptValue.fOperand.fS32 = s32;
+        element->setProperty(info->propertyIndex(), scriptValue);
+    }
+    return true;
+}
+
+bool SkAnimator::setInt(const char* id, const char* fieldID, int32_t s32) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setInt(element, field, s32);
+}
+
+bool SkAnimator::setScalar(SkDisplayable* element, const SkMemberInfo* info, SkScalar scalar) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        operand.fScalar = scalar;
+        SkASSERT(info->getType() == SkType_Float);
+        info->setValue(element, &operand, 1);
+    } else {
+        SkScriptValue scriptValue;
+        scriptValue.fType = SkType_Float;
+        scriptValue.fOperand.fScalar = scalar;
+        element->setProperty(info->propertyIndex(), scriptValue);
+    }
+    return true;
+}
+
+bool SkAnimator::setScalar(const char* id, const char* fieldID, SkScalar scalar) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setScalar(element, field, scalar);
+}
+
+bool SkAnimator::setString(SkDisplayable* element, 
+        const SkMemberInfo* info, const char* str) {
+    // !!! 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;
+}
+
+bool SkAnimator::setString(const char* id, const char* fieldID, const char* str) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setString(element, field, str);
+}
+
+void SkAnimator::setTimeline(const Timeline& timeline) {
+    fMaker->fTimeline = &timeline;
+}
+
+void SkAnimator::setURIBase(const char* uri) {
+    if (uri)
+    {
+        const char* tail = strrchr(uri, '/');
+        if (tail) {
+            SkString prefix(uri, tail - uri + 1);
+            if (uri[0] != '.' /*SkStream::IsAbsoluteURI(uri)*/)
+                fMaker->fPrefix.reset();
+            fMaker->fPrefix.append(prefix);
+            fMaker->fFileName.set(tail + 1);
+        } else
+            fMaker->fFileName.set(uri);
+    }
+}
+
+#ifdef SK_DEBUG
+bool SkAnimator::NoLeaks() {
+#ifdef SK_BUILD_FOR_MAC
+    if (SkDisplayable::fAllocations.count() == 0)
+        return true;
+//  return SkDisplayable::fAllocationCount == 0;
+    SkDebugf("!!! leaked %d displayables:\n", SkDisplayable::fAllocations.count());
+    for (SkDisplayable** leak = SkDisplayable::fAllocations.begin(); leak < SkDisplayable::fAllocations.end(); leak++)
+        SkDebugf("%08x %s\n", *leak, (*leak)->id);
+#endif
+    return false;
+}
+#endif
+
+#ifdef SK_SUPPORT_UNITTEST
+#include "SkAnimatorScript.h"
+#include "SkBase64.h"
+#include "SkParse.h"
+#include "SkMemberInfo.h"
+
+#define unittestline(type)  { #type , type::UnitTest }
+#endif
+
+
+void SkAnimator::Init(bool runUnitTests) {
+#ifdef SK_SUPPORT_UNITTEST
+    if (runUnitTests == false)
+        return;
+    static const struct {
+        const char* fTypeName;
+        void (*fUnitTest)( );
+    } gUnitTests[] = {
+        unittestline(SkBase64),
+        unittestline(SkDisplayType),
+        unittestline(SkParse),
+        unittestline(SkScriptEngine),
+//      unittestline(SkScriptEngine2),  // compiled script experiment
+        unittestline(SkAnimatorScript)
+    };
+    for (int i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++)
+    {
+        SkDebugf("SkAnimator: Running UnitTest for %s\n", gUnitTests[i].fTypeName);
+        gUnitTests[i].fUnitTest();
+        SkDebugf("SkAnimator: End UnitTest for %s\n", gUnitTests[i].fTypeName);
+    }
+#endif
+}
+
+void SkAnimator::Term() {
+}
+
+
+
diff --git a/legacy/src/animator/SkAnimatorScript.cpp b/legacy/src/animator/SkAnimatorScript.cpp
new file mode 100644
index 0000000..6aae006
--- /dev/null
+++ b/legacy/src/animator/SkAnimatorScript.cpp
@@ -0,0 +1,596 @@
+
+/*
+ * 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 "SkAnimatorScript.h"
+#include "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayTypes.h"
+#include "SkExtras.h"
+#include "SkMemberInfo.h"
+#include "SkParse.h"
+
+static const SkDisplayEnumMap gEnumMaps[] = {
+    { SkType_AddMode, "indirect|immediate" },
+    { SkType_Align, "left|center|right" },
+    { SkType_ApplyMode, "create|immediate|once" },
+    { SkType_ApplyTransition, "normal|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|leftSoftKey|rightSoftKey|home|back|send|end|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash|up|down|left|right|OK|volUp|volDown|camera" },
+    { SkType_EventKind, "none|keyChar|keyPress|keyPressUp|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" },
+    { SkType_EventMode, "deferred|immediate" },
+    { SkType_FillType, "winding|evenOdd" },
+    { SkType_FilterType, "none|bilinear" },
+    { SkType_FontStyle, "normal|bold|italic|boldItalic" },
+    { 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);
+
+SkAnimatorScript::SkAnimatorScript(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type)
+    : SkScriptEngine(SkScriptEngine::ToOpType(type)), fMaker(maker), fParent(NULL), fWorking(working)
+{
+    memberCallBack(EvalMember, (void*) this);
+    memberFunctionCallBack(EvalMemberFunction, (void*) this);
+    boxCallBack(Box, (void*) this);
+    unboxCallBack(Unbox, (void*) &maker);
+    propertyCallBack(EvalID, (void*) this); // must be first (entries are prepended, will be last), since it never fails
+    propertyCallBack(Infinity, (void*) this);
+    propertyCallBack(NaN, (void*) this);
+    functionCallBack(Eval, (void*) this);
+    functionCallBack(IsFinite, (void*) this);
+    functionCallBack(IsNaN, (void*) this);
+    if (type == SkType_ARGB) {
+        functionCallBack(EvalRGB, (void*) this);
+        propertyCallBack(EvalNamedColor, (void*) &maker.fIDs);
+    }
+    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); 
+    } 
+    for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) {
+        SkExtras* extra = *extraPtr;
+        if (extra->fExtraCallBack)
+            propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage);
+    }
+}
+
+SkAnimatorScript::~SkAnimatorScript() {
+    for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++)
+        delete *dispPtr;
+}
+
+bool SkAnimatorScript::evaluate(const char* original, SkScriptValue* result, SkDisplayTypes type) {
+        const char* script = original;
+        bool success = evaluateScript(&script, result);
+        if (success == false || result->fType != type) {
+            fMaker.setScriptError(*this);
+            return false;
+        }
+        return true;
+}
+
+bool SkAnimatorScript::Box(void* user, SkScriptValue* scriptValue) {
+    SkAnimatorScript* engine = (SkAnimatorScript*) user;
+    SkDisplayTypes type = scriptValue->fType;
+    SkDisplayable* displayable;
+    switch (type) {
+        case SkType_Array: {
+            SkDisplayArray* boxedValue = new SkDisplayArray(*scriptValue->fOperand.fArray);
+            displayable = boxedValue;
+            } break;
+        case SkType_Boolean: {
+            SkDisplayBoolean* boxedValue = new SkDisplayBoolean;
+            displayable = boxedValue;
+            boxedValue->value = !! scriptValue->fOperand.fS32;
+            } break;
+        case SkType_Int: {
+            SkDisplayInt* boxedValue = new SkDisplayInt;
+            displayable = boxedValue;
+            boxedValue->value = scriptValue->fOperand.fS32;
+            } break;
+        case SkType_Float: {
+            SkDisplayFloat* boxedValue = new SkDisplayFloat;
+            displayable = boxedValue;
+            boxedValue->value = scriptValue->fOperand.fScalar;
+            } break;
+        case SkType_String: {
+            SkDisplayString* boxedValue = new SkDisplayString(*scriptValue->fOperand.fString);
+            displayable = boxedValue;
+            } break;
+        case SkType_Displayable: 
+            scriptValue->fOperand.fObject = scriptValue->fOperand.fDisplayable;
+            scriptValue->fType = SkType_Displayable;
+            return true;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    engine->track(displayable);
+    scriptValue->fOperand.fObject = displayable;
+    scriptValue->fType = SkType_Displayable;
+    return true;
+}
+
+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;
+    if (params.count() != 1)
+        return false;
+    SkAnimatorScript* host = (SkAnimatorScript*) eng;
+    SkAnimatorScript engine(host->fMaker, host->fWorking, SkScriptEngine::ToDisplayType(host->fReturnType));
+    SkScriptValue* scriptValue = params.begin();
+    bool success = true;
+    if (scriptValue->fType == SkType_String) {
+        const char* script = scriptValue->fOperand.fString->c_str();
+        success = engine.evaluateScript(&script, value);
+    } else
+        *value = *scriptValue;
+    return success;
+}
+
+bool SkAnimatorScript::EvalEnum(const char* token, size_t len, void* callBack, SkScriptValue* value) {
+    const char* tokens = (const char*) callBack;
+    value->fType = SkType_Int;
+    if (MapEnums(tokens, token, len, (int*)&value->fOperand.fS32))
+        return true; 
+    return false;
+}
+
+bool SkAnimatorScript::EvalID(const char* token, size_t len, void* user, SkScriptValue* value) {
+    SkAnimatorScript* engine = (SkAnimatorScript*) user;
+    SkTDict<SkDisplayable*>* ids = &engine->fMaker.fIDs;
+    SkDisplayable* displayable;
+    bool success = ids->find(token, len, &displayable); 
+    if (success == false) {
+        displayable = engine->fWorking;
+        if (SK_LITERAL_STR_EQUAL("parent", token, len)) {
+            SkDisplayable* parent = displayable->getParent();
+            if (parent == false)
+                parent = engine->fParent;
+            if (parent) {
+                value->fOperand.fDisplayable = parent;
+                value->fType = SkType_Displayable;
+                return true;
+            }
+        }
+        if (displayable && EvalMember(token, len, displayable, engine, value))
+            return true;
+        value->fOperand.fString = NULL;
+        value->fType = SkType_String;   
+    } else {
+        SkDisplayable* working = engine->fWorking;
+        value->fOperand.fDisplayable = displayable;
+        value->fType = SkType_Displayable;
+        if (displayable->canContainDependents() && working && working->isAnimate()) {
+            SkAnimateBase* animator = (SkAnimateBase*) working;
+            if (animator->isDynamic()) {
+                SkDisplayDepend* depend = (SkDisplayDepend* ) displayable;
+                depend->addDependent(working);
+            }
+        }
+    }
+    return true;
+}
+
+bool SkAnimatorScript::EvalNamedColor(const char* token, size_t len, void* callback, SkScriptValue* value) {
+        value->fType = SkType_Int;
+    if (SkParse::FindNamedColor(token, len, (SkColor*) &value->fOperand.fS32) != NULL)
+        return true;
+    return false;
+}
+
+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;
+    if (params.count() != 3)
+        return false;
+    SkScriptEngine* engine = (SkScriptEngine*) eng;
+    unsigned result = 0xFF000000;
+    int shift = 16;
+    for (SkScriptValue* valuePtr = params.begin(); valuePtr < params.end(); valuePtr++) {
+        engine->convertTo(SkType_Int, valuePtr);
+        result |= SkClampMax(valuePtr->fOperand.fS32, 255) << shift;
+        shift -= 8;
+    }
+    value->fOperand.fS32 = result;
+    value->fType = SkType_Int;
+    return true;
+}
+
+bool SkAnimatorScript::EvalMemberCommon(SkScriptEngine* engine, const SkMemberInfo* info, 
+        SkDisplayable* displayable, SkScriptValue* 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->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 
+                type = SkType_Float;
+            }
+            break;
+        case SkType_String: {
+            SkString* displayableString;
+            if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) {
+                info->getString(displayable, &displayableString);
+                value->fOperand.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);
+            SkTypedArray* array = value->fOperand.fArray = new SkTypedArray(original);
+            engine->track(array);
+            int count = displayableArray->count();
+            if (count > 0) {
+                array->setCount(count);
+                memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand));
+            }
+            } break;
+        default:
+            SkASSERT(0); // unimplemented
+    }
+    value->fType = type;
+    return true;
+}
+
+bool SkAnimatorScript::EvalMember(const char* member, size_t len, void* object, void* eng, 
+        SkScriptValue* value) {
+    SkScriptEngine* engine = (SkScriptEngine*) eng;
+    SkDisplayable* displayable = (SkDisplayable*) object;
+    SkString name(member, len);
+    SkDisplayable* named = displayable->contains(name);
+    if (named) {
+        value->fOperand.fDisplayable = named;
+        value->fType = SkType_Displayable;
+        return true;
+    }
+    const SkMemberInfo* info = displayable->getMember(name.c_str());
+    if (info == NULL)
+        return false;
+    if (info->fType == SkType_MemberProperty) {
+        if (displayable->getProperty(info->propertyIndex(), value) == false) {
+            SkASSERT(0);
+            return false;
+        }
+    }
+    return EvalMemberCommon(engine, info, displayable, value);
+}
+
+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;
+    SkString name(member, len);
+    const SkMemberInfo* info = displayable->getMember(name.c_str());
+    SkASSERT(info != NULL); /* !!! error handling unimplemented */
+    if (info->fType != SkType_MemberFunction) {
+        SkASSERT(0);
+        return false;
+    }
+    displayable->executeFunction(displayable, info->functionIndex(), params, info->getType(), 
+        value);
+    return EvalMemberCommon(engine, info, displayable, value);
+}
+
+bool SkAnimatorScript::EvaluateDisplayable(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkDisplayable** result) {
+    SkAnimatorScript engine(maker, displayable, SkType_Displayable);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_Displayable);
+    if (success)
+        *result = value.fOperand.fDisplayable;
+    return success;
+}
+
+bool SkAnimatorScript::EvaluateInt(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, int32_t* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_Int);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_Int);
+    if (success)
+        *result = value.fOperand.fS32;
+    return success;
+}
+
+bool SkAnimatorScript::EvaluateFloat(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkScalar* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_Float);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_Float);
+    if (success)
+        *result = value.fOperand.fScalar;
+    return success;
+}
+
+bool SkAnimatorScript::EvaluateString(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkString* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_String);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_String);
+    if (success)
+        result->set(*(value.fOperand.fString));
+  return success;
+}
+
+bool SkAnimatorScript::EvaluateString(SkAnimateMaker& maker, SkDisplayable* displayable, SkDisplayable* parent, const char* script, SkString* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_String);
+    engine.fParent = parent;
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_String);
+    if (success)
+        result->set(*(value.fOperand.fString));
+  return success;
+}
+
+const SkDisplayEnumMap& SkAnimatorScript::GetEnumValues(SkDisplayTypes type) {
+    int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type, 
+        sizeof(SkDisplayEnumMap));
+    SkASSERT(index >= 0);
+    return gEnumMaps[index];
+}
+
+bool SkAnimatorScript::Infinity(const char* token, size_t len, void* user, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("Infinity", token, len) == false)
+        return false;
+    value->fType = SkType_Float;
+    value->fOperand.fScalar = SK_ScalarInfinity;
+    return true;
+}
+
+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;
+    if (params.count() != 1)
+        return false;
+    SkScriptValue* scriptValue = params.begin();
+    SkDisplayTypes type = scriptValue->fType;
+    SkScalar scalar = scriptValue->fOperand.fScalar;
+    value->fType = SkType_Int;
+    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, 
+        void* eng, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("isNaN", function, len) == false)
+        return false;
+    if (params.count() != 1)
+        return false;
+    SkScriptValue* scriptValue = params.begin();
+    value->fType = SkType_Int;
+    value->fOperand.fS32 = scriptValue->fType == SkType_Float ? SkScalarIsNaN(scriptValue->fOperand.fScalar) : 0;
+    return true;
+}
+
+bool SkAnimatorScript::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;
+}
+
+bool SkAnimatorScript::NaN(const char* token, size_t len, void* user, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("NaN", token, len) == false)
+        return false;
+    value->fType = SkType_Float;
+    value->fOperand.fScalar = SK_ScalarNaN;
+    return true;
+}
+
+#if 0
+bool SkAnimatorScript::ObjectToString(void* object, void* user, SkScriptValue* value) {
+    SkTDict<SkDisplayable*>* ids = (SkTDict<SkDisplayable*>*) user;
+    SkDisplayable* displayable = (SkDisplayable*) object;
+    const char* key;
+    bool success = ids->findKey(displayable, &key); 
+    if (success == false)
+        return false;
+    value->fOperand.fString =   new SkString(key);
+    value->fType = SkType_String;
+    return true;
+}
+#endif
+
+bool SkAnimatorScript::Unbox(void* m, SkScriptValue* scriptValue) {
+    SkAnimateMaker* maker = (SkAnimateMaker*) m;
+    SkASSERT((unsigned) scriptValue->fType == (unsigned) SkType_Displayable);
+    SkDisplayable* displayable = (SkDisplayable*) scriptValue->fOperand.fObject;
+    SkDisplayTypes type = displayable->getType();
+    switch (displayable->getType()) {
+        case SkType_Array: {
+            SkDisplayArray* boxedValue = (SkDisplayArray*) displayable;
+            scriptValue->fOperand.fArray = &boxedValue->values;
+            } break;
+        case SkType_Boolean: {
+            SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable;
+            scriptValue->fOperand.fS32 = boxedValue->value;
+            } break;
+        case SkType_Int: {
+            SkDisplayInt* boxedValue = (SkDisplayInt*) displayable;
+            scriptValue->fOperand.fS32 = boxedValue->value;
+            } break;
+        case SkType_Float: {
+            SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable;
+            scriptValue->fOperand.fScalar = boxedValue->value;
+            } break;
+        case SkType_String: {
+            SkDisplayString* boxedValue = (SkDisplayString*) displayable;
+            scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (boxedValue->value));
+            } break;
+        default: {
+            const char* id = NULL;
+            bool success = maker->findKey(displayable, &id);
+            SkASSERT(success);
+            scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (id));
+            type = SkType_String;
+        }
+    }
+    scriptValue->fType = type;
+    return true;
+}
+
+#if defined SK_SUPPORT_UNITTEST
+
+#include "SkAnimator.h"
+
+static const char scriptTestSetup[]  = 
+"<screenplay>\n"
+    "<text id='label' text='defg'/>\n"
+    "<add id='addLabel' use='label'/>\n"
+    "<text id='text1' text='test'/>\n"
+    "<apply scope='addLabel'>\n"
+        "<set target='label' field='text' to='#script:text1.text'/>\n"
+    "</apply>\n"
+    "<apply>\n"
+        "<paint id='labelPaint'>\n"
+            "<emboss id='emboss' direction='[1,1,1]'  />\n"
+        "</paint>\n"
+        "<animate id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>\n"
+        "<set lval='direction[0]' target='emboss' to='-1' />\n"
+    "</apply>\n"
+    "<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />\n"
+    "<color id='xColor' color='rgb(12,34,56)' />\n"
+    "<array id='emptyArray' />\n"
+    "<array id='intArray' values='[1, 4, 6]' />\n"
+    "<int id='idx' value='2' />\n"
+    "<int id='idy' value='2' />\n"
+    "<string id='alpha' value='abc' />\n"
+    "<rect id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />\n"
+    "<event id='evt'>\n"
+        "<input name='x' />\n"
+        "<apply scope='idy'>\n"
+            "<set field='value' to='evt.x.int' />\n"
+        "</apply>\n"
+    "</event>\n"
+"</screenplay>";
+
+#define DEFAULT_ANSWER   , 0
+
+static const SkScriptNAnswer scriptTests[]  = {
+    { "label.text.length == 4", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+//  { "labelPaint.measureText(label.text) > 0 ? labelPaint.measureText(label.text)+10 : 40", SkType_Float, 0, SkIntToScalar(0x23)  },
+    {   "Number.POSITIVE_INFINITY >= Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Infinity >= Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Number.NEGATIVE_INFINITY <= -Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Number.MIN_VALUE > 0 ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "isNaN(Number.NaN)", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "isNaN(NaN)", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) DEFAULT_ANSWER },
+    {   "alpha+alpha", SkType_String, 0, 0, "abcabc" },
+    {   "intArray[4]", SkType_Unknown DEFAULT_ANSWER DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "emptyArray[4]", SkType_Unknown DEFAULT_ANSWER DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "idx", SkType_Int, 2 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "intArray.length", SkType_Int, 3 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "intArray.values[0]", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "intArray[0]", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "idx.value", SkType_Int, 2 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "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 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "alpha.slice(1,2)", SkType_String, 0, 0, "b" },
+    {   "alpha.value.slice(1,2)", SkType_String, 0, 0, "b" },
+    {   "testRect.left+2", SkType_Float, 0, SkIntToScalar(3) DEFAULT_ANSWER },
+    {   "0 ? Math.sin(0) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? intArray[0] : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? intArray.values[0] : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? idx : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? idx.value : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? alpha.slice(1,2) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    { "idy", SkType_Int, 3 DEFAULT_ANSWER DEFAULT_ANSWER }
+};
+
+#define SkScriptNAnswer_testCount   SK_ARRAY_COUNT(scriptTests)
+
+void SkAnimatorScript::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 (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) {
+        SkAnimatorScript engine(*animator.fMaker, NULL, scriptTests[index].fType);
+        SkScriptValue value;
+        const char* script = scriptTests[index].fScript;
+        bool success = engine.evaluateScript(&script, &value);
+        if (success == false) {
+            SkDebugf("script failed: %s\n", scriptTests[index].fScript);
+            SkASSERT(scriptTests[index].fType == SkType_Unknown);
+            continue;
+        }
+        SkASSERT(value.fType == scriptTests[index].fType);
+        SkScalar error;
+        switch (value.fType) {
+            case SkType_Int:
+                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+                break;
+            case SkType_Float:
+                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
+                SkASSERT(error < SK_Scalar1 / 10000);
+                break;
+            case SkType_String:
+                SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0);
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+#endif
+}
+
+#endif
+
+
diff --git a/legacy/src/animator/SkAnimatorScript.h b/legacy/src/animator/SkAnimatorScript.h
new file mode 100644
index 0000000..a23f90d
--- /dev/null
+++ b/legacy/src/animator/SkAnimatorScript.h
@@ -0,0 +1,76 @@
+
+/*
+ * 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 SkAnimatorScript_DEFINED
+#define SkAnimatorScript_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkScript.h"
+#include "SkTypedArray.h"
+
+class SkAnimateMaker;
+struct SkMemberInfo;
+
+struct SkDisplayEnumMap {
+    SkDisplayTypes fType;
+    const char* fValues;
+};
+
+class SkAnimatorScript : public SkScriptEngine {
+public:
+    SkAnimatorScript(SkAnimateMaker& , SkDisplayable* , SkDisplayTypes type);
+    ~SkAnimatorScript();
+    bool evaluate(const char* script, SkScriptValue* , SkDisplayTypes type);
+    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* );
+    static bool EvaluateInt(SkAnimateMaker& , SkDisplayable* , const char* script, int32_t* );
+    static bool EvaluateString(SkAnimateMaker& , SkDisplayable* , const char* script, SkString* );
+    static bool EvaluateString(SkAnimateMaker& , SkDisplayable* , SkDisplayable* parent, const char* script, SkString* );
+    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, 
+        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, 
+        SkScriptValue* value);
+    static bool EvalMemberCommon(SkScriptEngine* , const SkMemberInfo* info, 
+        SkDisplayable* displayable, SkScriptValue* value);
+    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, 
+        void* callBack, SkScriptValue* );
+    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, 
+        void* callBack, SkScriptValue* );
+    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);
+    SkTDDisplayableArray fTrackDisplayable;
+    SkAnimateMaker& fMaker;
+    SkDisplayable* fParent;
+    SkDisplayable* fWorking;
+private:
+    friend class SkDump;
+    friend struct SkScriptNAnswer;
+#ifdef SK_SUPPORT_UNITTEST
+public:
+    static void UnitTest();
+#endif
+};
+
+#endif // SkAnimatorScript_DEFINED
+
diff --git a/legacy/src/animator/SkAnimatorScript2.cpp b/legacy/src/animator/SkAnimatorScript2.cpp
new file mode 100644
index 0000000..ae51fdc
--- /dev/null
+++ b/legacy/src/animator/SkAnimatorScript2.cpp
@@ -0,0 +1,623 @@
+
+/*
+ * 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 "SkAnimatorScript2.h"
+#include "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayTypes.h"
+#include "SkExtras.h"
+#include "SkMemberInfo.h"
+#include "SkOpArray.h"
+#include "SkParse.h"
+#include "SkScript2.h"
+#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" },
+};
+
+static int gEnumMapCount = SK_ARRAY_COUNT(gEnumMaps);
+
+
+class SkAnimatorScript_Box : public SkScriptCallBackConvert {
+public:
+	SkAnimatorScript_Box() {}
+
+	~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 SkOperand2::OpType getReturnType(int index) { 
+		return SkOperand2::kObject; 
+	}
+
+	virtual Type getType() const { 
+		return kBox; 
+	}
+
+	void track(SkDisplayable* displayable) { 
+		SkASSERT(fTrackDisplayable.find(displayable) < 0);  
+		*fTrackDisplayable.append() = displayable; 
+	}
+
+	SkTDDisplayableArray fTrackDisplayable;
+};
+
+
+class SkAnimatorScript_Enum : public SkScriptCallBackProperty {
+public:
+	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);
+	}
+
+private:
+	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)
+class SkAnimatorScript_Eval : public SkScriptCallBackFunction {
+public:
+	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 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;
+};
+
+class SkAnimatorScript_ID : public SkScriptCallBackProperty {
+public:
+	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 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;
+	}
+
+private:
+	SkAnimatorScript2* fEngine;
+};
+
+
+class SkAnimatorScript_Member : public SkScriptCallBackMember {
+public:
+
+	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 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;
+};
+
+
+class SkAnimatorScript_MemberFunction : public SkScriptCallBackMemberFunction {
+public:
+	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;
+	}
+
+	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);
+	}
+
+	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;
+	}
+};
+
+
+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 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;
+	}
+
+};
+
+
+class SkAnimatorScript_Unbox : public SkScriptCallBackConvert {
+public:
+	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 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; 
+	}
+
+	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);
+	}
+#endif
+}
+
+SkAnimatorScript2::~SkAnimatorScript2() {
+	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;
+}
+
+const SkDisplayEnumMap& SkAnimatorScript2::GetEnumValues(SkDisplayTypes type) {
+	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;
+	}
+}
+
+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;
+	}
+}
+
+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;
+}
+
+#if defined SK_DEBUG
+
+#include "SkAnimator.h"
+
+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>"
+"</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 }
+};
+
+#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);
+		}
+	}
+#endif
+}
+
+#endif
+
diff --git a/legacy/src/animator/SkAnimatorScript2.h b/legacy/src/animator/SkAnimatorScript2.h
new file mode 100644
index 0000000..e6bf58e
--- /dev/null
+++ b/legacy/src/animator/SkAnimatorScript2.h
@@ -0,0 +1,50 @@
+
+/*
+ * 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 SkAnimatorScript2_DEFINED
+#define SkAnimatorScript2_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkScript2.h"
+#include "SkTypedArray.h"
+
+class SkAnimateMaker;
+struct SkMemberInfo;
+
+#ifndef SkAnimatorScript_DEFINED
+struct SkDisplayEnumMap {
+	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);
+private:
+	SkAnimateMaker& fMaker;
+	SkDisplayable* fWorking;
+	friend class SkDump;
+	friend struct SkScriptNAnswer;
+	// illegal
+	SkAnimatorScript2& operator=(const SkAnimatorScript2&);
+#ifdef SK_DEBUG
+public:
+	static void UnitTest();
+#endif
+};
+
+#endif // SkAnimatorScript2_DEFINED
diff --git a/legacy/src/animator/SkBoundable.cpp b/legacy/src/animator/SkBoundable.cpp
new file mode 100644
index 0000000..017b6b8
--- /dev/null
+++ b/legacy/src/animator/SkBoundable.cpp
@@ -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.
+ */
+
+
+#include "SkBoundable.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+
+SkBoundable::SkBoundable() {
+    clearBounds();
+    fBounds.fTop = 0;
+    fBounds.fRight = 0;
+    fBounds.fBottom = 0;
+}
+
+void SkBoundable::clearBounder() {
+    fBounds.fLeft = 0x7fff;
+}
+
+void SkBoundable::getBounds(SkRect* rect) {
+    SkASSERT(rect);
+    if (fBounds.fLeft == (int16_t)0x8000U) {
+        INHERITED::getBounds(rect);
+        return;
+    }
+    rect->fLeft = SkIntToScalar(fBounds.fLeft);
+    rect->fTop = SkIntToScalar(fBounds.fTop);
+    rect->fRight = SkIntToScalar(fBounds.fRight);
+    rect->fBottom = SkIntToScalar(fBounds.fBottom);
+}
+
+void SkBoundable::enableBounder() {
+    fBounds.fLeft = 0;
+}
+
+
+SkBoundableAuto::SkBoundableAuto(SkBoundable* boundable, 
+        SkAnimateMaker& maker) : fBoundable(boundable), fMaker(maker) {
+    if (fBoundable->hasBounds()) {
+        fMaker.fCanvas->setBounder(&maker.fDisplayList);
+        fMaker.fDisplayList.fBounds.setEmpty();
+    }
+}
+
+SkBoundableAuto::~SkBoundableAuto() {
+    if (fBoundable->hasBounds() == false)
+        return;
+    fMaker.fCanvas->setBounder(NULL);
+    fBoundable->setBounds(fMaker.fDisplayList.fBounds);
+}
+
diff --git a/legacy/src/animator/SkBoundable.h b/legacy/src/animator/SkBoundable.h
new file mode 100644
index 0000000..1e70505
--- /dev/null
+++ b/legacy/src/animator/SkBoundable.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 SkBoundable_DEFINED
+#define SkBoundable_DEFINED
+
+#include "SkDrawable.h"
+#include "SkRect.h"
+
+class SkBoundable : public SkDrawable {
+public:
+    SkBoundable();
+    virtual void clearBounder();
+    virtual void enableBounder();
+    virtual void getBounds(SkRect* );
+    bool hasBounds() { return fBounds.fLeft != (int16_t)0x8000U; }
+    void setBounds(SkIRect& bounds) { fBounds = bounds; }
+protected:
+    void clearBounds() { fBounds.fLeft = (int16_t) SkToU16(0x8000); }; // mark bounds as unset
+    SkIRect fBounds;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+class SkBoundableAuto {
+public:
+    SkBoundableAuto(SkBoundable* boundable, SkAnimateMaker& maker);
+    ~SkBoundableAuto();
+private:
+    SkBoundable* fBoundable;
+    SkAnimateMaker& fMaker;
+    SkBoundableAuto& operator= (const SkBoundableAuto& );
+};
+
+#endif // SkBoundable_DEFINED
+
diff --git a/legacy/src/animator/SkBuildCondensedInfo.cpp b/legacy/src/animator/SkBuildCondensedInfo.cpp
new file mode 100644
index 0000000..30da67e
--- /dev/null
+++ b/legacy/src/animator/SkBuildCondensedInfo.cpp
@@ -0,0 +1,284 @@
+
+/*
+ * 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 "SkTypes.h"
+#if defined SK_BUILD_CONDENSED
+#include "SkMemberInfo.h"
+#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
+#error "SK_BUILD_FOR_WIN32 must be defined to build condensed info"
+#endif
+#include "SkDisplayType.h"
+#include "SkIntArray.h"
+#include <stdio.h>
+
+SkTDMemberInfoArray gInfos;
+SkTDIntArray gInfosCounts;
+SkTDDisplayTypesArray gInfosTypeIDs;
+SkTDMemberInfoArray gUnknowns;
+SkTDIntArray gUnknownsCounts;
+
+static void AddInfo(SkDisplayTypes type, const SkMemberInfo* info, int infoCount) {
+    SkASSERT(gInfos[type] == NULL);
+    gInfos[type] = info;
+    gInfosCounts[type] = infoCount;
+    *gInfosTypeIDs.append() = type;
+    size_t allStrs = 0;
+    for (int inner = 0; inner < infoCount; inner++) {
+        SkASSERT(info[inner].fCount < 256);
+        int offset = (int) info[inner].fOffset;
+        SkASSERT(offset < 128 && offset > -129);
+        SkASSERT(allStrs < 256);
+        if (info[inner].fType == SkType_BaseClassInfo) {
+            const SkMemberInfo* innerInfo = (const SkMemberInfo*) info[inner].fName;
+            if (gUnknowns.find(innerInfo) == -1) {
+                *gUnknowns.append() = innerInfo;
+                *gUnknownsCounts.append() = info[inner].fCount;
+            }
+        }
+        if (info[inner].fType != SkType_BaseClassInfo && info[inner].fName)
+            allStrs += strlen(info[inner].fName);
+        allStrs += 1;
+        SkASSERT(info[inner].fType < 256);
+    }
+}
+
+static void WriteInfo(FILE* condensed, const SkMemberInfo* info, int infoCount,
+            const char* typeName, bool draw, bool display) {
+    fprintf(condensed, "static const char g%sStrings[] = \n", typeName);
+    int inner;
+    // write strings
+    for (inner = 0; inner < infoCount; inner++) {
+        const char* name = (info[inner].fType != SkType_BaseClassInfo && info[inner].fName) ?
+            info[inner].fName : "";
+        const char* zero = inner < infoCount - 1 ? "\\0" : "";
+        fprintf(condensed, "\t\"%s%s\"\n", name, zero);
+    }
+    fprintf(condensed, ";\n\nstatic const SkMemberInfo g%s", draw ? "Draw" : display ? "Display" : "");
+    fprintf(condensed, "%sInfo[] = {", typeName);
+    size_t nameOffset = 0;
+    // write info tables
+    for (inner = 0; inner < infoCount; inner++) {
+        size_t offset = info[inner].fOffset;
+        if (info[inner].fType == SkType_BaseClassInfo) {
+            offset = (size_t) gInfos.find((const SkMemberInfo* ) info[inner].fName);
+            SkASSERT((int) offset >= 0);
+            offset = gInfosTypeIDs.find((SkDisplayTypes) offset);
+            SkASSERT((int) offset >= 0);
+        }
+        fprintf(condensed, "\n\t{%d, %d, %d, %d}", nameOffset, offset,
+            info[inner].fType, info[inner].fCount);
+        if (inner < infoCount - 1)
+            putc(',', condensed);
+        if (info[inner].fType != SkType_BaseClassInfo && info[inner].fName)
+            nameOffset += strlen(info[inner].fName);
+        nameOffset += 1;
+    }
+    fprintf(condensed, "\n};\n\n");
+}
+
+static void Get3DName(char* scratch, const char* name) {
+    if (strncmp("skia3d:", name, sizeof("skia3d:") - 1) == 0) {
+        strcpy(scratch, "3D_");
+        scratch[3]= name[7] & ~0x20;
+        strcpy(&scratch[4], &name[8]);
+    } else {
+        scratch[0] = name[0] & ~0x20;
+        strcpy(&scratch[1], &name[1]);
+    }
+}
+
+int type_compare(const void* a, const void* b) {
+    SkDisplayTypes first = *(SkDisplayTypes*) a;
+    SkDisplayTypes second = *(SkDisplayTypes*) b;
+    return first < second ? -1 : first == second ? 0 : 1;
+}
+
+void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* maker) {
+    gInfos.setCount(kNumberOfTypes);
+    memset(gInfos.begin(), 0, sizeof(gInfos[0]) * kNumberOfTypes);
+    gInfosCounts.setCount(kNumberOfTypes);
+    memset(gInfosCounts.begin(), -1, sizeof(gInfosCounts[0]) * kNumberOfTypes);
+    // check to see if it is condensable
+    int index, infoCount;
+    for (index = 0; index < kTypeNamesSize; index++) {
+        const SkMemberInfo* info = GetMembers(maker, gTypeNames[index].fType, &infoCount);
+        if (info == NULL)
+            continue;
+        AddInfo(gTypeNames[index].fType, info, infoCount);
+    }
+    const SkMemberInfo* extraInfo = 
+        SkDisplayType::GetMembers(maker, SkType_3D_Point, &infoCount);
+    AddInfo(SkType_Point, extraInfo, infoCount);
+    AddInfo(SkType_3D_Point, extraInfo, infoCount);
+//  int baseInfos = gInfos.count();
+    do {
+        SkTDMemberInfoArray oldRefs = gUnknowns;
+        SkTDIntArray oldRefCounts = gUnknownsCounts;
+        gUnknowns.reset();
+        gUnknownsCounts.reset();
+        for (index = 0; index < oldRefs.count(); index++) {
+            const SkMemberInfo* info = oldRefs[index];
+            if (gInfos.find(info) == -1) {
+                int typeIndex = 0;
+                for (; typeIndex < kNumberOfTypes; typeIndex++) {
+                    const SkMemberInfo* temp = SkDisplayType::GetMembers(
+                        maker, (SkDisplayTypes) typeIndex, NULL);
+                    if (temp == info)
+                        break;
+                }
+                SkASSERT(typeIndex < kNumberOfTypes);
+                AddInfo((SkDisplayTypes) typeIndex, info, oldRefCounts[index]);
+            }
+        }
+    } while (gUnknowns.count() > 0);
+    qsort(gInfosTypeIDs.begin(), gInfosTypeIDs.count(), sizeof(gInfosTypeIDs[0]), &type_compare);
+#ifdef SK_DEBUG
+    FILE* condensed = fopen("../../src/animator/SkCondensedDebug.cpp", "w+");
+    fprintf(condensed, "#include \"SkTypes.h\"\n");
+    fprintf(condensed, "#ifdef SK_DEBUG\n");
+#else
+    FILE* condensed = fopen("../../src/animator/SkCondensedRelease.cpp", "w+");
+    fprintf(condensed, "#include \"SkTypes.h\"\n");
+    fprintf(condensed, "#ifdef SK_RELEASE\n");
+#endif
+    // write header
+    fprintf(condensed, "// This file was automatically generated.\n");
+    fprintf(condensed, "// To change it, edit the file with the matching debug info.\n");
+    fprintf(condensed, "// Then execute SkDisplayType::BuildCondensedInfo() to "
+        "regenerate this file.\n\n");
+    // write name of memberInfo
+    int typeNameIndex = 0;
+    int unknown = 1;
+    for (index = 0; index < gInfos.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        char scratch[64];
+        bool drawPrefix, displayPrefix;
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+            drawPrefix = gTypeNames[typeNameIndex].fDrawPrefix;
+            displayPrefix = gTypeNames[typeNameIndex].fDisplayPrefix;
+        } else {
+            sprintf(scratch, "Unknown%d", unknown++);
+            drawPrefix = displayPrefix = false;
+        }
+        WriteInfo(condensed, info, gInfosCounts[index], scratch, drawPrefix, displayPrefix);
+    }
+    // write array of table pointers
+//  start here;
+    fprintf(condensed, "static const SkMemberInfo* const gInfoTables[] = {");
+    typeNameIndex = 0;
+    unknown = 1;
+    for (index = 0; index < gInfos.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        char scratch[64];
+        bool drawPrefix, displayPrefix;
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+            drawPrefix = gTypeNames[typeNameIndex].fDrawPrefix;
+            displayPrefix = gTypeNames[typeNameIndex].fDisplayPrefix;
+        } else {
+            sprintf(scratch, "Unknown%d", unknown++);
+            drawPrefix = displayPrefix = false;
+        }
+        fprintf(condensed, "\n\tg");
+        if (drawPrefix)
+            fprintf(condensed, "Draw");
+        if (displayPrefix)
+            fprintf(condensed, "Display");
+        fprintf(condensed, "%sInfo", scratch);
+        if (index < gInfos.count() - 1)
+                putc(',', condensed);
+    }
+    fprintf(condensed, "\n};\n\n");
+    // write the array of number of entries in the info table
+    fprintf(condensed, "static const unsigned char gInfoCounts[] = {\n\t");
+    int written = 0;
+    for (index = 0; index < gInfosCounts.count(); index++) {
+        int count = gInfosCounts[index];
+        if (count < 0)
+            continue;
+        if (written > 0)
+            putc(',', condensed);
+        if (written % 20 == 19)
+            fprintf(condensed, "\n\t");
+        fprintf(condensed, "%d",count);
+        written++;
+    }
+    fprintf(condensed, "\n};\n\n");
+    // write array of type ids table entries correspond to
+    fprintf(condensed, "static const unsigned char gTypeIDs[] = {\n\t");
+    int typeIDCount = 0;
+    typeNameIndex = 0;
+    unknown = 1;
+    for (index = 0; index < gInfosCounts.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        typeIDCount++;
+        char scratch[64];
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+        } else
+            sprintf(scratch, "Unknown%d", unknown++);
+        fprintf(condensed, "%d%c // %s\n\t", index, 
+            index < gInfosCounts.count() ? ',' : ' ', scratch);
+    }
+    fprintf(condensed, "\n};\n\n");
+    fprintf(condensed, "static const int kTypeIDs = %d;\n\n", typeIDCount);
+    // write the array of string pointers
+    fprintf(condensed, "static const char* const gInfoNames[] = {");
+    typeNameIndex = 0;
+    unknown = 1;
+    written = 0;
+    for (index = 0; index < gInfosCounts.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        if (written > 0)
+                putc(',', condensed);
+        written++;
+        fprintf(condensed, "\n\tg");
+        char scratch[64];
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+        } else
+            sprintf(scratch, "Unknown%d", unknown++);
+        fprintf(condensed, "%sStrings", scratch);
+    }
+    fprintf(condensed, "\n};\n\n");
+    fprintf(condensed, "#endif\n");
+    fclose(condensed);
+    gInfos.reset();
+    gInfosCounts.reset();
+    gInfosTypeIDs.reset();
+    gUnknowns.reset();
+    gUnknownsCounts.reset();
+}
+
+#elif defined SK_DEBUG
+#include "SkDisplayType.h"
+void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* ) {}
+#endif
+
+
diff --git a/legacy/src/animator/SkCondensedDebug.cpp b/legacy/src/animator/SkCondensedDebug.cpp
new file mode 100644
index 0000000..c1c7007
--- /dev/null
+++ b/legacy/src/animator/SkCondensedDebug.cpp
@@ -0,0 +1,1389 @@
+
+/*
+ * 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 "SkTypes.h"
+#ifndef SK_BUILD_FOR_UNIX
+#ifdef SK_DEBUG
+// This file was automatically generated.
+// To change it, edit the file with the matching debug info.
+// Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file.
+
+static const char gMathStrings[] = 
+    "E\0"
+    "LN10\0"
+    "LN2\0"
+    "LOG10E\0"
+    "LOG2E\0"
+    "PI\0"
+    "SQRT1_2\0"
+    "SQRT2\0"
+    "abs\0"
+    "acos\0"
+    "asin\0"
+    "atan\0"
+    "atan2\0"
+    "ceil\0"
+    "cos\0"
+    "exp\0"
+    "floor\0"
+    "log\0"
+    "max\0"
+    "min\0"
+    "pow\0"
+    "random\0"
+    "round\0"
+    "sin\0"
+    "sqrt\0"
+    "tan"
+;
+
+static const SkMemberInfo gMathInfo[] = {
+    {0, -1, 67, 98},
+    {2, -2, 67, 98},
+    {7, -3, 67, 98},
+    {11, -4, 67, 98},
+    {18, -5, 67, 98},
+    {24, -6, 67, 98},
+    {27, -7, 67, 98},
+    {35, -8, 67, 98},
+    {41, -1, 66, 98},
+    {45, -2, 66, 98},
+    {50, -3, 66, 98},
+    {55, -4, 66, 98},
+    {60, -5, 66, 98},
+    {66, -6, 66, 98},
+    {71, -7, 66, 98},
+    {75, -8, 66, 98},
+    {79, -9, 66, 98},
+    {85, -10, 66, 98},
+    {89, -11, 66, 98},
+    {93, -12, 66, 98},
+    {97, -13, 66, 98},
+    {101, -14, 66, 98},
+    {108, -15, 66, 98},
+    {114, -16, 66, 98},
+    {118, -17, 66, 98},
+    {123, -18, 66, 98}
+};
+
+static const char gAddStrings[] = 
+    "inPlace\0"
+    "offset\0"
+    "use\0"
+    "where"
+;
+
+static const SkMemberInfo gAddInfo[] = {
+    {0, 16, 26, 1},
+    {8, 20, 96, 1},
+    {15, 24, 37, 1},
+    {19, 28, 37, 1}
+};
+
+static const char gAddCircleStrings[] = 
+    "\0"
+    "radius\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gAddCircleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 24, 98, 1},
+    {8, 28, 98, 1},
+    {10, 32, 98, 1}
+};
+
+static const char gUnknown1Strings[] = 
+    "direction"
+;
+
+static const SkMemberInfo gUnknown1Info[] = {
+    {0, 20, 75, 1}
+};
+
+static const char gAddOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAddOvalInfo[] = {
+    {0, 6, 18, 5}
+};
+
+static const char gAddPathStrings[] = 
+    "matrix\0"
+    "path"
+;
+
+static const SkMemberInfo gAddPathInfo[] = {
+    {0, 20, 65, 1},
+    {7, 24, 74, 1}
+};
+
+static const char gAddRectangleStrings[] = 
+    "\0"
+    "bottom\0"
+    "left\0"
+    "right\0"
+    "top"
+;
+
+static const SkMemberInfo gAddRectangleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 36, 98, 1},
+    {8, 24, 98, 1},
+    {13, 32, 98, 1},
+    {19, 28, 98, 1}
+};
+
+static const char gAddRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gAddRoundRectInfo[] = {
+    {0, 6, 18, 5},
+    {1, 40, 98, 1},
+    {4, 44, 98, 1}
+};
+
+static const char gUnknown2Strings[] = 
+    "begin\0"
+    "blend\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "from\0"
+    "mirror\0"
+    "repeat\0"
+    "reset\0"
+    "target\0"
+    "to\0"
+    "values"
+;
+
+static const SkMemberInfo gUnknown2Info[] = {
+    {0, 16, 71, 1},
+    {6, 20, 119, 98},
+    {12, 36, 71, 1},
+    {16, -1, 67, 26},
+    {24, 40, 108, 2},
+    {30, 48, 40, 2},
+    {38, 56, 40, 2},
+    {43, -2, 67, 26},
+    {50, 64, 98, 1},
+    {57, -3, 67, 26},
+    {63, 68, 40, 2},
+    {70, 76, 40, 2},
+    {73, -4, 67, 40}
+};
+
+static const char gAnimateFieldStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAnimateFieldInfo[] = {
+    {0, 8, 18, 13}
+};
+
+static const char gApplyStrings[] = 
+    "animator\0"
+    "begin\0"
+    "dontDraw\0"
+    "dynamicScope\0"
+    "interval\0"
+    "mode\0"
+    "pickup\0"
+    "restore\0"
+    "scope\0"
+    "step\0"
+    "steps\0"
+    "time\0"
+    "transition"
+;
+
+static const SkMemberInfo gApplyInfo[] = {
+    {0, -1, 67, 10},
+    {9, 16, 71, 1},
+    {15, 20, 26, 1},
+    {24, 24, 108, 2},
+    {37, 32, 71, 1},
+    {46, 36, 13, 1},
+    {51, 40, 26, 1},
+    {58, 44, 26, 1},
+    {66, 48, 37, 1},
+    {72, -2, 67, 96},
+    {77, 52, 96, 1},
+    {83, -3, 67, 71},
+    {88, 56, 14, 1}
+};
+
+static const char gUnknown3Strings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gUnknown3Info[] = {
+    {0, 48, 98, 1},
+    {2, 52, 98, 1}
+};
+
+static const char gBitmapStrings[] = 
+    "\0"
+    "erase\0"
+    "format\0"
+    "height\0"
+    "rowBytes\0"
+    "width"
+;
+
+static const SkMemberInfo gDrawBitmapInfo[] = {
+    {0, 11, 18, 2},
+    {1, -1, 67, 15},
+    {7, 56, 21, 1},
+    {14, 60, 96, 1},
+    {21, 64, 96, 1},
+    {30, 68, 96, 1}
+};
+
+static const char gBitmapShaderStrings[] = 
+    "\0"
+    "filterType\0"
+    "image"
+;
+
+static const SkMemberInfo gDrawBitmapShaderInfo[] = {
+    {0, 67, 18, 2},
+    {1, 28, 47, 1},
+    {12, 32, 17, 1}
+};
+
+static const char gBlurStrings[] = 
+    "blurStyle\0"
+    "radius"
+;
+
+static const SkMemberInfo gDrawBlurInfo[] = {
+    {0, 24, 63, 1},
+    {10, 20, 98, 1}
+};
+
+static const char gBoundsStrings[] = 
+    "\0"
+    "inval"
+;
+
+static const SkMemberInfo gDisplayBoundsInfo[] = {
+    {0, 58, 18, 7},
+    {1, 44, 26, 1}
+};
+
+static const char gClipStrings[] = 
+    "path\0"
+    "rectangle"
+;
+
+static const SkMemberInfo gDrawClipInfo[] = {
+    {0, 20, 74, 1},
+    {5, 16, 91, 1}
+};
+
+static const char gColorStrings[] = 
+    "alpha\0"
+    "blue\0"
+    "color\0"
+    "green\0"
+    "hue\0"
+    "red\0"
+    "saturation\0"
+    "value"
+;
+
+static const SkMemberInfo gDrawColorInfo[] = {
+    {0, -1, 67, 98},
+    {6, -2, 67, 98},
+    {11, 20, 15, 1},
+    {17, -3, 67, 98},
+    {23, -4, 67, 98},
+    {27, -5, 67, 98},
+    {31, -6, 67, 98},
+    {42, -7, 67, 98}
+};
+
+static const char gCubicToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "x3\0"
+    "y1\0"
+    "y2\0"
+    "y3"
+;
+
+static const SkMemberInfo gCubicToInfo[] = {
+    {0, 20, 98, 1},
+    {3, 28, 98, 1},
+    {6, 36, 98, 1},
+    {9, 24, 98, 1},
+    {12, 32, 98, 1},
+    {15, 40, 98, 1}
+};
+
+static const char gDashStrings[] = 
+    "intervals\0"
+    "phase"
+;
+
+static const SkMemberInfo gDashInfo[] = {
+    {0, 20, 119, 98},
+    {10, 36, 98, 1}
+};
+
+static const char gDataStrings[] = 
+    "\0"
+    "name"
+;
+
+static const SkMemberInfo gDataInfo[] = {
+    {0, 33, 18, 3},
+    {1, 32, 108, 2}
+};
+
+static const char gDiscreteStrings[] = 
+    "deviation\0"
+    "segLength"
+;
+
+static const SkMemberInfo gDiscreteInfo[] = {
+    {0, 20, 98, 1},
+    {10, 24, 98, 1}
+};
+
+static const char gDrawToStrings[] = 
+    "drawOnce\0"
+    "use"
+;
+
+static const SkMemberInfo gDrawToInfo[] = {
+    {0, 72, 26, 1},
+    {9, 76, 19, 1}
+};
+
+static const char gDumpStrings[] = 
+    "displayList\0"
+    "eventList\0"
+    "events\0"
+    "groups\0"
+    "name\0"
+    "posts"
+;
+
+static const SkMemberInfo gDumpInfo[] = {
+    {0, 16, 26, 1},
+    {12, 20, 26, 1},
+    {22, 24, 26, 1},
+    {29, 36, 26, 1},
+    {36, 28, 108, 2},
+    {41, 40, 26, 1}
+};
+
+static const char gEmbossStrings[] = 
+    "ambient\0"
+    "direction\0"
+    "radius\0"
+    "specular"
+;
+
+static const SkMemberInfo gDrawEmbossInfo[] = {
+    {0, -1, 67, 98},
+    {8, 20, 119, 98},
+    {18, 36, 98, 1},
+    {25, -2, 67, 98}
+};
+
+static const char gEventStrings[] = 
+    "code\0"
+    "disable\0"
+    "key\0"
+    "keys\0"
+    "kind\0"
+    "target\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDisplayEventInfo[] = {
+    {0, 16, 43, 1},
+    {5, 20, 26, 1},
+    {13, -1, 67, 108},
+    {17, -2, 67, 108},
+    {22, 24, 44, 1},
+    {27, 28, 108, 2},
+    {34, 36, 98, 1},
+    {36, 40, 98, 1}
+};
+
+static const char gFromPathStrings[] = 
+    "mode\0"
+    "offset\0"
+    "path"
+;
+
+static const SkMemberInfo gFromPathInfo[] = {
+    {0, 20, 49, 1},
+    {5, 24, 98, 1},
+    {12, 28, 74, 1}
+};
+
+static const char gUnknown4Strings[] = 
+    "\0"
+    "offsets\0"
+    "unitMapper"
+;
+
+static const SkMemberInfo gUnknown4Info[] = {
+    {0, 67, 18, 2},
+    {1, 28, 119, 98},
+    {9, 44, 108, 2}
+};
+
+static const char gGStrings[] = 
+    "condition\0"
+    "enableCondition"
+;
+
+static const SkMemberInfo gGInfo[] = {
+    {0, 16, 40, 2},
+    {10, 24, 40, 2}
+};
+
+static const char gHitClearStrings[] = 
+    "targets"
+;
+
+static const SkMemberInfo gHitClearInfo[] = {
+    {0, 16, 119, 36}
+};
+
+static const char gHitTestStrings[] = 
+    "bullets\0"
+    "hits\0"
+    "targets\0"
+    "value"
+;
+
+static const SkMemberInfo gHitTestInfo[] = {
+    {0, 16, 119, 36},
+    {8, 32, 119, 96},
+    {13, 48, 119, 36},
+    {21, 64, 26, 1}
+};
+
+static const char gImageStrings[] = 
+    "\0"
+    "base64\0"
+    "src"
+;
+
+static const SkMemberInfo gImageInfo[] = {
+    {0, 11, 18, 2},
+    {1, 56, 16, 2},
+    {8, 64, 108, 2}
+};
+
+static const char gIncludeStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gIncludeInfo[] = {
+    {0, 16, 108, 2}
+};
+
+static const char gInputStrings[] = 
+    "s32\0"
+    "scalar\0"
+    "string"
+;
+
+static const SkMemberInfo gInputInfo[] = {
+    {0, 16, 96, 1},
+    {4, 20, 98, 1},
+    {11, 24, 108, 2}
+};
+
+static const char gLineStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gLineInfo[] = {
+    {0, 24, 98, 1},
+    {3, 28, 98, 1},
+    {6, 32, 98, 1},
+    {9, 36, 98, 1}
+};
+
+static const char gLineToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gLineToInfo[] = {
+    {0, 20, 98, 1},
+    {2, 24, 98, 1}
+};
+
+static const char gLinearGradientStrings[] = 
+    "\0"
+    "points"
+;
+
+static const SkMemberInfo gLinearGradientInfo[] = {
+    {0, 27, 18, 3},
+    {1, 88, 77, 4}
+};
+
+static const char gMatrixStrings[] = 
+    "matrix\0"
+    "perspectX\0"
+    "perspectY\0"
+    "rotate\0"
+    "scale\0"
+    "scaleX\0"
+    "scaleY\0"
+    "skewX\0"
+    "skewY\0"
+    "translate\0"
+    "translateX\0"
+    "translateY"
+;
+
+static const SkMemberInfo gDrawMatrixInfo[] = {
+    {0, 16, 119, 98},
+    {7, -1, 67, 98},
+    {17, -2, 67, 98},
+    {27, -3, 67, 98},
+    {34, -4, 67, 98},
+    {40, -5, 67, 98},
+    {47, -6, 67, 98},
+    {54, -7, 67, 98},
+    {60, -8, 67, 98},
+    {66, -9, 67, 77},
+    {76, -10, 67, 98},
+    {87, -11, 67, 98}
+};
+
+static const char gMoveStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gMoveInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gMoveToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gMoveToInfo[] = {
+    {0, 20, 98, 1},
+    {2, 24, 98, 1}
+};
+
+static const char gMovieStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gMovieInfo[] = {
+    {0, 16, 108, 2}
+};
+
+static const char gOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gOvalInfo[] = {
+    {0, 58, 18, 7}
+};
+
+static const char gPaintStrings[] = 
+    "antiAlias\0"
+    "ascent\0"
+    "color\0"
+    "descent\0"
+    "filterType\0"
+    "linearText\0"
+    "maskFilter\0"
+    "measureText\0"
+    "pathEffect\0"
+    "shader\0"
+    "strikeThru\0"
+    "stroke\0"
+    "strokeCap\0"
+    "strokeJoin\0"
+    "strokeMiter\0"
+    "strokeWidth\0"
+    "style\0"
+    "textAlign\0"
+    "textScaleX\0"
+    "textSize\0"
+    "textSkewX\0"
+    "textTracking\0"
+    "typeface\0"
+    "underline\0"
+    "xfermode"
+;
+
+static const SkMemberInfo gDrawPaintInfo[] = {
+    {0, 16, 26, 1},
+    {10, -1, 67, 98},
+    {17, 20, 31, 1},
+    {23, -2, 67, 98},
+    {31, 24, 47, 1},
+    {42, 28, 26, 1},
+    {53, 32, 62, 1},
+    {64, -1, 66, 98},
+    {76, 36, 76, 1},
+    {87, 40, 102, 1},
+    {94, 44, 26, 1},
+    {105, 48, 26, 1},
+    {112, 52, 27, 1},
+    {122, 56, 58, 1},
+    {133, 60, 98, 1},
+    {145, 64, 98, 1},
+    {157, 68, 109, 1},
+    {163, 72, 9, 1},
+    {173, 76, 98, 1},
+    {184, 80, 98, 1},
+    {193, 84, 98, 1},
+    {203, 88, 98, 1},
+    {216, 92, 120, 1},
+    {225, 96, 26, 1},
+    {235, 100, 121, 1}
+};
+
+static const char gPathStrings[] = 
+    "d\0"
+    "fillType\0"
+    "length"
+;
+
+static const SkMemberInfo gDrawPathInfo[] = {
+    {0, 52, 108, 2},
+    {2, -1, 67, 46},
+    {11, -2, 67, 98}
+};
+
+static const char gUnknown5Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown5Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gPointStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDrawPointInfo[] = {
+    {0, 16, 98, 1},
+    {2, 20, 98, 1}
+};
+
+static const char gPolyToPolyStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gPolyToPolyInfo[] = {
+    {0, 24, 80, 1},
+    {12, 20, 80, 1}
+};
+
+static const char gPolygonStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gPolygonInfo[] = {
+    {0, 48, 18, 1}
+};
+
+static const char gPolylineStrings[] = 
+    "points"
+;
+
+static const SkMemberInfo gPolylineInfo[] = {
+    {0, 88, 119, 98}
+};
+
+static const char gPostStrings[] = 
+    "delay\0"
+    "initialized\0"
+    "mode\0"
+    "sink\0"
+    "target\0"
+    "type"
+;
+
+static const SkMemberInfo gPostInfo[] = {
+    {0, 16, 71, 1},
+    {6, 20, 26, 1},
+    {18, 24, 45, 1},
+    {23, -1, 67, 108},
+    {28, -2, 67, 108},
+    {35, -3, 67, 108}
+};
+
+static const char gQuadToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gQuadToInfo[] = {
+    {0, 20, 98, 1},
+    {3, 28, 98, 1},
+    {6, 24, 98, 1},
+    {9, 32, 98, 1}
+};
+
+static const char gRCubicToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRCubicToInfo[] = {
+    {0, 18, 18, 6}
+};
+
+static const char gRLineToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRLineToInfo[] = {
+    {0, 35, 18, 2}
+};
+
+static const char gRMoveToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRMoveToInfo[] = {
+    {0, 39, 18, 2}
+};
+
+static const char gRQuadToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRQuadToInfo[] = {
+    {0, 50, 18, 4}
+};
+
+static const char gRadialGradientStrings[] = 
+    "\0"
+    "center\0"
+    "radius"
+;
+
+static const SkMemberInfo gRadialGradientInfo[] = {
+    {0, 27, 18, 3},
+    {1, 88, 77, 2},
+    {8, 96, 98, 1}
+};
+
+static const char gRandomStrings[] = 
+    "blend\0"
+    "max\0"
+    "min\0"
+    "random\0"
+    "seed"
+;
+
+static const SkMemberInfo gDisplayRandomInfo[] = {
+    {0, 16, 98, 1},
+    {6, 24, 98, 1},
+    {10, 20, 98, 1},
+    {14, 1, 67, 98},
+    {21, -2, 67, 96}
+};
+
+static const char gRectToRectStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gRectToRectInfo[] = {
+    {0, 24, 91, 1},
+    {12, 20, 91, 1}
+};
+
+static const char gRectangleStrings[] = 
+    "bottom\0"
+    "height\0"
+    "left\0"
+    "needsRedraw\0"
+    "right\0"
+    "top\0"
+    "width"
+;
+
+static const SkMemberInfo gRectangleInfo[] = {
+    {0, 36, 98, 1},
+    {7, -1, 67, 98},
+    {14, 24, 98, 1},
+    {19, -2, 67, 26},
+    {31, 32, 98, 1},
+    {37, 28, 98, 1},
+    {41, -3, 67, 98}
+};
+
+static const char gRemoveStrings[] = 
+    "offset\0"
+    "where"
+;
+
+static const SkMemberInfo gRemoveInfo[] = {
+    {0, 20, 96, 1},
+    {7, 28, 37, 1}
+};
+
+static const char gReplaceStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gReplaceInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gRotateStrings[] = 
+    "center\0"
+    "degrees"
+;
+
+static const SkMemberInfo gRotateInfo[] = {
+    {0, 24, 77, 2},
+    {7, 20, 98, 1}
+};
+
+static const char gRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gRoundRectInfo[] = {
+    {0, 58, 18, 7},
+    {1, 44, 98, 1},
+    {4, 48, 98, 1}
+};
+
+static const char gS32Strings[] = 
+    "value"
+;
+
+static const SkMemberInfo gS32Info[] = {
+    {0, 16, 96, 1}
+};
+
+static const char gScalarStrings[] = 
+    "value"
+;
+
+static const SkMemberInfo gScalarInfo[] = {
+    {0, 16, 98, 1}
+};
+
+static const char gScaleStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gScaleInfo[] = {
+    {0, 28, 77, 2},
+    {7, 20, 98, 1},
+    {9, 24, 98, 1}
+};
+
+static const char gSetStrings[] = 
+    "begin\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "reset\0"
+    "target\0"
+    "to"
+;
+
+static const SkMemberInfo gSetInfo[] = {
+    {0, 16, 71, 1},
+    {6, 36, 71, 1},
+    {10, -1, 67, 26},
+    {18, 40, 108, 2},
+    {24, 48, 40, 2},
+    {32, -3, 67, 26},
+    {38, 68, 40, 2},
+    {45, 76, 40, 2}
+};
+
+static const char gShaderStrings[] = 
+    "matrix\0"
+    "tileMode"
+;
+
+static const SkMemberInfo gShaderInfo[] = {
+    {0, 20, 65, 1},
+    {7, 24, 116, 1}
+};
+
+static const char gSkewStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gSkewInfo[] = {
+    {0, 28, 77, 2},
+    {7, 20, 98, 1},
+    {9, 24, 98, 1}
+};
+
+static const char g3D_CameraStrings[] = 
+    "axis\0"
+    "hackHeight\0"
+    "hackWidth\0"
+    "location\0"
+    "observer\0"
+    "patch\0"
+    "zenith"
+;
+
+static const SkMemberInfo g3D_CameraInfo[] = {
+    {0, 36, 106, 3},
+    {5, 20, 98, 1},
+    {16, 16, 98, 1},
+    {26, 24, 106, 3},
+    {35, 60, 106, 3},
+    {44, 108, 105, 1},
+    {50, 48, 106, 3}
+};
+
+static const char g3D_PatchStrings[] = 
+    "origin\0"
+    "rotateDegrees\0"
+    "u\0"
+    "v"
+;
+
+static const SkMemberInfo g3D_PatchInfo[] = {
+    {0, 40, 106, 3},
+    {7, -1, 66, 98},
+    {21, 16, 106, 3},
+    {23, 28, 106, 3}
+};
+
+static const char gUnknown6Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown6Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gSnapshotStrings[] = 
+    "filename\0"
+    "quality\0"
+    "sequence\0"
+    "type"
+;
+
+static const SkMemberInfo gSnapshotInfo[] = {
+    {0, 16, 108, 2},
+    {9, 24, 98, 1},
+    {17, 28, 26, 1},
+    {26, 32, 20, 1}
+};
+
+static const char gStringStrings[] = 
+    "length\0"
+    "slice\0"
+    "value"
+;
+
+static const SkMemberInfo gStringInfo[] = {
+    {0, -1, 67, 96},
+    {7, -1, 66, 108},
+    {13, 16, 108, 2}
+};
+
+static const char gTextStrings[] = 
+    "length\0"
+    "text\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTextInfo[] = {
+    {0, -1, 67, 96},
+    {7, 24, 108, 2},
+    {12, 32, 98, 1},
+    {14, 36, 98, 1}
+};
+
+static const char gTextBoxStrings[] = 
+    "\0"
+    "mode\0"
+    "spacingAdd\0"
+    "spacingAlign\0"
+    "spacingMul\0"
+    "text"
+;
+
+static const SkMemberInfo gTextBoxInfo[] = {
+    {0, 58, 18, 7},
+    {1, 60, 113, 1},
+    {6, 56, 98, 1},
+    {17, 64, 112, 1},
+    {30, 52, 98, 1},
+    {41, 44, 108, 2}
+};
+
+static const char gTextOnPathStrings[] = 
+    "offset\0"
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextOnPathInfo[] = {
+    {0, 24, 98, 1},
+    {7, 28, 74, 1},
+    {12, 32, 110, 1}
+};
+
+static const char gTextToPathStrings[] = 
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextToPathInfo[] = {
+    {0, 16, 74, 1},
+    {5, 20, 110, 1}
+};
+
+static const char gTranslateStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTranslateInfo[] = {
+    {0, 20, 98, 1},
+    {2, 24, 98, 1}
+};
+
+static const char gTypedArrayStrings[] = 
+    "length\0"
+    "values"
+;
+
+static const SkMemberInfo gTypedArrayInfo[] = {
+    {0, -1, 67, 96},
+    {7, 16, 119, 0}
+};
+
+static const char gTypefaceStrings[] = 
+    "fontName"
+;
+
+static const SkMemberInfo gTypefaceInfo[] = {
+    {0, 20, 108, 2}
+};
+
+static const SkMemberInfo* const gInfoTables[] = {
+    gMathInfo,
+    gAddInfo,
+    gAddCircleInfo,
+    gUnknown1Info,
+    gAddOvalInfo,
+    gAddPathInfo,
+    gAddRectangleInfo,
+    gAddRoundRectInfo,
+    gUnknown2Info,
+    gAnimateFieldInfo,
+    gApplyInfo,
+    gUnknown3Info,
+    gDrawBitmapInfo,
+    gDrawBitmapShaderInfo,
+    gDrawBlurInfo,
+    gDisplayBoundsInfo,
+    gDrawClipInfo,
+    gDrawColorInfo,
+    gCubicToInfo,
+    gDashInfo,
+    gDataInfo,
+    gDiscreteInfo,
+    gDrawToInfo,
+    gDumpInfo,
+    gDrawEmbossInfo,
+    gDisplayEventInfo,
+    gFromPathInfo,
+    gUnknown4Info,
+    gGInfo,
+    gHitClearInfo,
+    gHitTestInfo,
+    gImageInfo,
+    gIncludeInfo,
+    gInputInfo,
+    gLineInfo,
+    gLineToInfo,
+    gLinearGradientInfo,
+    gDrawMatrixInfo,
+    gMoveInfo,
+    gMoveToInfo,
+    gMovieInfo,
+    gOvalInfo,
+    gDrawPaintInfo,
+    gDrawPathInfo,
+    gUnknown5Info,
+    gDrawPointInfo,
+    gPolyToPolyInfo,
+    gPolygonInfo,
+    gPolylineInfo,
+    gPostInfo,
+    gQuadToInfo,
+    gRCubicToInfo,
+    gRLineToInfo,
+    gRMoveToInfo,
+    gRQuadToInfo,
+    gRadialGradientInfo,
+    gDisplayRandomInfo,
+    gRectToRectInfo,
+    gRectangleInfo,
+    gRemoveInfo,
+    gReplaceInfo,
+    gRotateInfo,
+    gRoundRectInfo,
+    gS32Info,
+    gScalarInfo,
+    gScaleInfo,
+    gSetInfo,
+    gShaderInfo,
+    gSkewInfo,
+    g3D_CameraInfo,
+    g3D_PatchInfo,
+    gUnknown6Info,
+    gSnapshotInfo,
+    gStringInfo,
+    gTextInfo,
+    gTextBoxInfo,
+    gTextOnPathInfo,
+    gTextToPathInfo,
+    gTranslateInfo,
+    gTypedArrayInfo,
+    gTypefaceInfo,
+};
+
+static const unsigned char gInfoCounts[] = {
+    26,4,4,1,1,2,5,3,13,1,13,2,6,3,2,2,2,8,6,
+    2,2,2,2,6,4,8,3,3,2,1,4,3,1,3,4,2,2,12,1,
+    2,1,1,25,3,3,2,2,1,1,6,4,1,1,1,1,3,5,2,7,
+    2,1,2,3,1,1,3,8,2,3,7,4,3,4,3,4,6,3,2,2,
+    2,1
+};
+
+static const unsigned char gTypeIDs[] = {
+    1, // Math
+    2, // Add
+    3, // AddCircle
+    4, // Unknown1
+    5, // AddOval
+    6, // AddPath
+    7, // AddRectangle
+    8, // AddRoundRect
+    10, // Unknown2
+    11, // AnimateField
+    12, // Apply
+    17, // Unknown3
+    19, // Bitmap
+    22, // BitmapShader
+    23, // Blur
+    25, // Bounds
+    29, // Clip
+    31, // Color
+    32, // CubicTo
+    33, // Dash
+    34, // Data
+    35, // Discrete
+    38, // DrawTo
+    39, // Dump
+    41, // Emboss
+    42, // Event
+    48, // FromPath
+    51, // Unknown4
+    52, // G
+    53, // HitClear
+    54, // HitTest
+    55, // Image
+    56, // Include
+    57, // Input
+    59, // Line
+    60, // LineTo
+    61, // LinearGradient
+    65, // Matrix
+    68, // Move
+    69, // MoveTo
+    70, // Movie
+    72, // Oval
+    73, // Paint
+    74, // Path
+    77, // Unknown5
+    78, // Point
+    79, // PolyToPoly
+    80, // Polygon
+    81, // Polyline
+    82, // Post
+    83, // QuadTo
+    84, // RCubicTo
+    85, // RLineTo
+    86, // RMoveTo
+    87, // RQuadTo
+    88, // RadialGradient
+    89, // Random
+    90, // RectToRect
+    91, // Rectangle
+    92, // Remove
+    93, // Replace
+    94, // Rotate
+    95, // RoundRect
+    96, // S32
+    98, // Scalar
+    99, // Scale
+    101, // Set
+    102, // Shader
+    103, // Skew
+    104, // 3D_Camera
+    105, // 3D_Patch
+    106, // Unknown6
+    107, // Snapshot
+    108, // String
+    110, // Text
+    111, // TextBox
+    114, // TextOnPath
+    115, // TextToPath
+    117, // Translate
+    119, // TypedArray
+    120, // Typeface
+    
+};
+
+static const int kTypeIDs = 81;
+
+static const char* const gInfoNames[] = {
+    gMathStrings,
+    gAddStrings,
+    gAddCircleStrings,
+    gUnknown1Strings,
+    gAddOvalStrings,
+    gAddPathStrings,
+    gAddRectangleStrings,
+    gAddRoundRectStrings,
+    gUnknown2Strings,
+    gAnimateFieldStrings,
+    gApplyStrings,
+    gUnknown3Strings,
+    gBitmapStrings,
+    gBitmapShaderStrings,
+    gBlurStrings,
+    gBoundsStrings,
+    gClipStrings,
+    gColorStrings,
+    gCubicToStrings,
+    gDashStrings,
+    gDataStrings,
+    gDiscreteStrings,
+    gDrawToStrings,
+    gDumpStrings,
+    gEmbossStrings,
+    gEventStrings,
+    gFromPathStrings,
+    gUnknown4Strings,
+    gGStrings,
+    gHitClearStrings,
+    gHitTestStrings,
+    gImageStrings,
+    gIncludeStrings,
+    gInputStrings,
+    gLineStrings,
+    gLineToStrings,
+    gLinearGradientStrings,
+    gMatrixStrings,
+    gMoveStrings,
+    gMoveToStrings,
+    gMovieStrings,
+    gOvalStrings,
+    gPaintStrings,
+    gPathStrings,
+    gUnknown5Strings,
+    gPointStrings,
+    gPolyToPolyStrings,
+    gPolygonStrings,
+    gPolylineStrings,
+    gPostStrings,
+    gQuadToStrings,
+    gRCubicToStrings,
+    gRLineToStrings,
+    gRMoveToStrings,
+    gRQuadToStrings,
+    gRadialGradientStrings,
+    gRandomStrings,
+    gRectToRectStrings,
+    gRectangleStrings,
+    gRemoveStrings,
+    gReplaceStrings,
+    gRotateStrings,
+    gRoundRectStrings,
+    gS32Strings,
+    gScalarStrings,
+    gScaleStrings,
+    gSetStrings,
+    gShaderStrings,
+    gSkewStrings,
+    g3D_CameraStrings,
+    g3D_PatchStrings,
+    gUnknown6Strings,
+    gSnapshotStrings,
+    gStringStrings,
+    gTextStrings,
+    gTextBoxStrings,
+    gTextOnPathStrings,
+    gTextToPathStrings,
+    gTranslateStrings,
+    gTypedArrayStrings,
+    gTypefaceStrings
+};
+
+#endif
+#endif
+
+
diff --git a/legacy/src/animator/SkCondensedRelease.cpp b/legacy/src/animator/SkCondensedRelease.cpp
new file mode 100644
index 0000000..234e67e
--- /dev/null
+++ b/legacy/src/animator/SkCondensedRelease.cpp
@@ -0,0 +1,1366 @@
+
+/*
+ * 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 "SkTypes.h"
+#ifndef SK_BUILD_FOR_UNIX
+#ifdef SK_RELEASE
+// This file was automatically generated.
+// To change it, edit the file with the matching debug info.
+// Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file.
+
+static const char gMathStrings[] = 
+    "E\0"
+    "LN10\0"
+    "LN2\0"
+    "LOG10E\0"
+    "LOG2E\0"
+    "PI\0"
+    "SQRT1_2\0"
+    "SQRT2\0"
+    "abs\0"
+    "acos\0"
+    "asin\0"
+    "atan\0"
+    "atan2\0"
+    "ceil\0"
+    "cos\0"
+    "exp\0"
+    "floor\0"
+    "log\0"
+    "max\0"
+    "min\0"
+    "pow\0"
+    "random\0"
+    "round\0"
+    "sin\0"
+    "sqrt\0"
+    "tan"
+;
+
+static const SkMemberInfo gMathInfo[] = {
+    {0, -1, 67, 98},
+    {2, -2, 67, 98},
+    {7, -3, 67, 98},
+    {11, -4, 67, 98},
+    {18, -5, 67, 98},
+    {24, -6, 67, 98},
+    {27, -7, 67, 98},
+    {35, -8, 67, 98},
+    {41, -1, 66, 98},
+    {45, -2, 66, 98},
+    {50, -3, 66, 98},
+    {55, -4, 66, 98},
+    {60, -5, 66, 98},
+    {66, -6, 66, 98},
+    {71, -7, 66, 98},
+    {75, -8, 66, 98},
+    {79, -9, 66, 98},
+    {85, -10, 66, 98},
+    {89, -11, 66, 98},
+    {93, -12, 66, 98},
+    {97, -13, 66, 98},
+    {101, -14, 66, 98},
+    {108, -15, 66, 98},
+    {114, -16, 66, 98},
+    {118, -17, 66, 98},
+    {123, -18, 66, 98}
+};
+
+static const char gAddStrings[] = 
+    "inPlace\0"
+    "offset\0"
+    "use\0"
+    "where"
+;
+
+static const SkMemberInfo gAddInfo[] = {
+    {0, 4, 26, 1},
+    {8, 8, 96, 1},
+    {15, 12, 37, 1},
+    {19, 16, 37, 1}
+};
+
+static const char gAddCircleStrings[] = 
+    "\0"
+    "radius\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gAddCircleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 12, 98, 1},
+    {8, 16, 98, 1},
+    {10, 20, 98, 1}
+};
+
+static const char gUnknown1Strings[] = 
+    "direction"
+;
+
+static const SkMemberInfo gUnknown1Info[] = {
+    {0, 8, 75, 1}
+};
+
+static const char gAddOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAddOvalInfo[] = {
+    {0, 6, 18, 5}
+};
+
+static const char gAddPathStrings[] = 
+    "matrix\0"
+    "path"
+;
+
+static const SkMemberInfo gAddPathInfo[] = {
+    {0, 8, 65, 1},
+    {7, 12, 74, 1}
+};
+
+static const char gAddRectangleStrings[] = 
+    "\0"
+    "bottom\0"
+    "left\0"
+    "right\0"
+    "top"
+;
+
+static const SkMemberInfo gAddRectangleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 24, 98, 1},
+    {8, 12, 98, 1},
+    {13, 20, 98, 1},
+    {19, 16, 98, 1}
+};
+
+static const char gAddRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gAddRoundRectInfo[] = {
+    {0, 6, 18, 5},
+    {1, 28, 98, 1},
+    {4, 32, 98, 1}
+};
+
+static const char gUnknown2Strings[] = 
+    "begin\0"
+    "blend\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "from\0"
+    "mirror\0"
+    "repeat\0"
+    "reset\0"
+    "target\0"
+    "to\0"
+    "values"
+;
+
+static const SkMemberInfo gUnknown2Info[] = {
+    {0, 4, 71, 1},
+    {6, 8, 119, 98},
+    {12, 16, 71, 1},
+    {16, -1, 67, 26},
+    {24, 20, 108, 1},
+    {30, 24, 40, 1},
+    {38, 28, 40, 1},
+    {43, -2, 67, 26},
+    {50, 32, 98, 1},
+    {57, -3, 67, 26},
+    {63, 36, 40, 1},
+    {70, 40, 40, 1},
+    {73, -4, 67, 40}
+};
+
+static const char gAnimateFieldStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAnimateFieldInfo[] = {
+    {0, 8, 18, 13}
+};
+
+static const char gApplyStrings[] = 
+    "animator\0"
+    "begin\0"
+    "dontDraw\0"
+    "dynamicScope\0"
+    "interval\0"
+    "mode\0"
+    "pickup\0"
+    "restore\0"
+    "scope\0"
+    "step\0"
+    "steps\0"
+    "time\0"
+    "transition"
+;
+
+static const SkMemberInfo gApplyInfo[] = {
+    {0, -1, 67, 10},
+    {9, 4, 71, 1},
+    {15, 8, 26, 1},
+    {24, 12, 108, 1},
+    {37, 16, 71, 1},
+    {46, 20, 13, 1},
+    {51, 24, 26, 1},
+    {58, 28, 26, 1},
+    {66, 32, 37, 1},
+    {72, -2, 67, 96},
+    {77, 36, 96, 1},
+    {83, -3, 67, 71},
+    {88, 40, 14, 1}
+};
+
+static const char gUnknown3Strings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gUnknown3Info[] = {
+    {0, 36, 98, 1},
+    {2, 40, 98, 1}
+};
+
+static const char gBitmapStrings[] = 
+    "\0"
+    "erase\0"
+    "format\0"
+    "height\0"
+    "rowBytes\0"
+    "width"
+;
+
+static const SkMemberInfo gDrawBitmapInfo[] = {
+    {0, 11, 18, 2},
+    {1, -1, 67, 15},
+    {7, 44, 21, 1},
+    {14, 48, 96, 1},
+    {21, 52, 96, 1},
+    {30, 56, 96, 1}
+};
+
+static const char gBitmapShaderStrings[] = 
+    "\0"
+    "filterType\0"
+    "image"
+;
+
+static const SkMemberInfo gDrawBitmapShaderInfo[] = {
+    {0, 66, 18, 2},
+    {1, 16, 47, 1},
+    {12, 20, 17, 1}
+};
+
+static const char gBlurStrings[] = 
+    "blurStyle\0"
+    "radius"
+;
+
+static const SkMemberInfo gDrawBlurInfo[] = {
+    {0, 12, 63, 1},
+    {10, 8, 98, 1}
+};
+
+static const char gBoundsStrings[] = 
+    "\0"
+    "inval"
+;
+
+static const SkMemberInfo gDisplayBoundsInfo[] = {
+    {0, 57, 18, 7},
+    {1, 32, 26, 1}
+};
+
+static const char gClipStrings[] = 
+    "path\0"
+    "rectangle"
+;
+
+static const SkMemberInfo gDrawClipInfo[] = {
+    {0, 8, 74, 1},
+    {5, 4, 91, 1}
+};
+
+static const char gColorStrings[] = 
+    "alpha\0"
+    "blue\0"
+    "color\0"
+    "green\0"
+    "hue\0"
+    "red\0"
+    "saturation\0"
+    "value"
+;
+
+static const SkMemberInfo gDrawColorInfo[] = {
+    {0, -1, 67, 98},
+    {6, -2, 67, 98},
+    {11, 8, 15, 1},
+    {17, -3, 67, 98},
+    {23, -4, 67, 98},
+    {27, -5, 67, 98},
+    {31, -6, 67, 98},
+    {42, -7, 67, 98}
+};
+
+static const char gCubicToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "x3\0"
+    "y1\0"
+    "y2\0"
+    "y3"
+;
+
+static const SkMemberInfo gCubicToInfo[] = {
+    {0, 8, 98, 1},
+    {3, 16, 98, 1},
+    {6, 24, 98, 1},
+    {9, 12, 98, 1},
+    {12, 20, 98, 1},
+    {15, 28, 98, 1}
+};
+
+static const char gDashStrings[] = 
+    "intervals\0"
+    "phase"
+;
+
+static const SkMemberInfo gDashInfo[] = {
+    {0, 8, 119, 98},
+    {10, 16, 98, 1}
+};
+
+static const char gDataStrings[] = 
+    "\0"
+    "name"
+;
+
+static const SkMemberInfo gDataInfo[] = {
+    {0, 32, 18, 3},
+    {1, 16, 108, 1}
+};
+
+static const char gDiscreteStrings[] = 
+    "deviation\0"
+    "segLength"
+;
+
+static const SkMemberInfo gDiscreteInfo[] = {
+    {0, 8, 98, 1},
+    {10, 12, 98, 1}
+};
+
+static const char gDrawToStrings[] = 
+    "drawOnce\0"
+    "use"
+;
+
+static const SkMemberInfo gDrawToInfo[] = {
+    {0, 36, 26, 1},
+    {9, 40, 19, 1}
+};
+
+static const char gEmbossStrings[] = 
+    "ambient\0"
+    "direction\0"
+    "radius\0"
+    "specular"
+;
+
+static const SkMemberInfo gDrawEmbossInfo[] = {
+    {0, -1, 67, 98},
+    {8, 8, 119, 98},
+    {18, 16, 98, 1},
+    {25, -2, 67, 98}
+};
+
+static const char gEventStrings[] = 
+    "code\0"
+    "disable\0"
+    "key\0"
+    "keys\0"
+    "kind\0"
+    "target\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDisplayEventInfo[] = {
+    {0, 4, 43, 1},
+    {5, 8, 26, 1},
+    {13, -1, 67, 108},
+    {17, -2, 67, 108},
+    {22, 12, 44, 1},
+    {27, 16, 108, 1},
+    {34, 20, 98, 1},
+    {36, 24, 98, 1}
+};
+
+static const char gFromPathStrings[] = 
+    "mode\0"
+    "offset\0"
+    "path"
+;
+
+static const SkMemberInfo gFromPathInfo[] = {
+    {0, 8, 49, 1},
+    {5, 12, 98, 1},
+    {12, 16, 74, 1}
+};
+
+static const char gUnknown4Strings[] = 
+    "\0"
+    "offsets\0"
+    "unitMapper"
+;
+
+static const SkMemberInfo gUnknown4Info[] = {
+    {0, 66, 18, 2},
+    {1, 16, 119, 98},
+    {9, 24, 108, 1}
+};
+
+static const char gGStrings[] = 
+    "condition\0"
+    "enableCondition"
+;
+
+static const SkMemberInfo gGInfo[] = {
+    {0, 4, 40, 1},
+    {10, 8, 40, 1}
+};
+
+static const char gHitClearStrings[] = 
+    "targets"
+;
+
+static const SkMemberInfo gHitClearInfo[] = {
+    {0, 4, 119, 36}
+};
+
+static const char gHitTestStrings[] = 
+    "bullets\0"
+    "hits\0"
+    "targets\0"
+    "value"
+;
+
+static const SkMemberInfo gHitTestInfo[] = {
+    {0, 4, 119, 36},
+    {8, 12, 119, 96},
+    {13, 20, 119, 36},
+    {21, 28, 26, 1}
+};
+
+static const char gImageStrings[] = 
+    "\0"
+    "base64\0"
+    "src"
+;
+
+static const SkMemberInfo gImageInfo[] = {
+    {0, 11, 18, 2},
+    {1, 44, 16, 2},
+    {8, 52, 108, 1}
+};
+
+static const char gIncludeStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gIncludeInfo[] = {
+    {0, 4, 108, 1}
+};
+
+static const char gInputStrings[] = 
+    "s32\0"
+    "scalar\0"
+    "string"
+;
+
+static const SkMemberInfo gInputInfo[] = {
+    {0, 4, 96, 1},
+    {4, 8, 98, 1},
+    {11, 12, 108, 1}
+};
+
+static const char gLineStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gLineInfo[] = {
+    {0, 12, 98, 1},
+    {3, 16, 98, 1},
+    {6, 20, 98, 1},
+    {9, 24, 98, 1}
+};
+
+static const char gLineToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gLineToInfo[] = {
+    {0, 8, 98, 1},
+    {2, 12, 98, 1}
+};
+
+static const char gLinearGradientStrings[] = 
+    "\0"
+    "points"
+;
+
+static const SkMemberInfo gLinearGradientInfo[] = {
+    {0, 26, 18, 3},
+    {1, 48, 77, 4}
+};
+
+static const char gMatrixStrings[] = 
+    "matrix\0"
+    "perspectX\0"
+    "perspectY\0"
+    "rotate\0"
+    "scale\0"
+    "scaleX\0"
+    "scaleY\0"
+    "skewX\0"
+    "skewY\0"
+    "translate\0"
+    "translateX\0"
+    "translateY"
+;
+
+static const SkMemberInfo gDrawMatrixInfo[] = {
+    {0, 4, 119, 98},
+    {7, -1, 67, 98},
+    {17, -2, 67, 98},
+    {27, -3, 67, 98},
+    {34, -4, 67, 98},
+    {40, -5, 67, 98},
+    {47, -6, 67, 98},
+    {54, -7, 67, 98},
+    {60, -8, 67, 98},
+    {66, -9, 67, 77},
+    {76, -10, 67, 98},
+    {87, -11, 67, 98}
+};
+
+static const char gMoveStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gMoveInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gMoveToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gMoveToInfo[] = {
+    {0, 8, 98, 1},
+    {2, 12, 98, 1}
+};
+
+static const char gMovieStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gMovieInfo[] = {
+    {0, 4, 108, 1}
+};
+
+static const char gOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gOvalInfo[] = {
+    {0, 57, 18, 7}
+};
+
+static const char gPaintStrings[] = 
+    "antiAlias\0"
+    "ascent\0"
+    "color\0"
+    "descent\0"
+    "filterType\0"
+    "linearText\0"
+    "maskFilter\0"
+    "measureText\0"
+    "pathEffect\0"
+    "shader\0"
+    "strikeThru\0"
+    "stroke\0"
+    "strokeCap\0"
+    "strokeJoin\0"
+    "strokeMiter\0"
+    "strokeWidth\0"
+    "style\0"
+    "textAlign\0"
+    "textScaleX\0"
+    "textSize\0"
+    "textSkewX\0"
+    "textTracking\0"
+    "typeface\0"
+    "underline\0"
+    "xfermode"
+;
+
+static const SkMemberInfo gDrawPaintInfo[] = {
+    {0, 4, 26, 1},
+    {10, -1, 67, 98},
+    {17, 8, 31, 1},
+    {23, -2, 67, 98},
+    {31, 12, 47, 1},
+    {42, 16, 26, 1},
+    {53, 20, 62, 1},
+    {64, -1, 66, 98},
+    {76, 24, 76, 1},
+    {87, 28, 102, 1},
+    {94, 32, 26, 1},
+    {105, 36, 26, 1},
+    {112, 40, 27, 1},
+    {122, 44, 58, 1},
+    {133, 48, 98, 1},
+    {145, 52, 98, 1},
+    {157, 56, 109, 1},
+    {163, 60, 9, 1},
+    {173, 64, 98, 1},
+    {184, 68, 98, 1},
+    {193, 72, 98, 1},
+    {203, 76, 98, 1},
+    {216, 80, 120, 1},
+    {225, 84, 26, 1},
+    {235, 88, 121, 1}
+};
+
+static const char gPathStrings[] = 
+    "d\0"
+    "fillType\0"
+    "length"
+;
+
+static const SkMemberInfo gDrawPathInfo[] = {
+    {0, 32, 108, 1},
+    {2, -1, 67, 46},
+    {11, -2, 67, 98}
+};
+
+static const char gUnknown5Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown5Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gPointStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDrawPointInfo[] = {
+    {0, 4, 98, 1},
+    {2, 8, 98, 1}
+};
+
+static const char gPolyToPolyStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gPolyToPolyInfo[] = {
+    {0, 12, 80, 1},
+    {12, 8, 80, 1}
+};
+
+static const char gPolygonStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gPolygonInfo[] = {
+    {0, 47, 18, 1}
+};
+
+static const char gPolylineStrings[] = 
+    "points"
+;
+
+static const SkMemberInfo gPolylineInfo[] = {
+    {0, 56, 119, 98}
+};
+
+static const char gPostStrings[] = 
+    "delay\0"
+    "initialized\0"
+    "mode\0"
+    "sink\0"
+    "target\0"
+    "type"
+;
+
+static const SkMemberInfo gPostInfo[] = {
+    {0, 4, 71, 1},
+    {6, 8, 26, 1},
+    {18, 12, 45, 1},
+    {23, -1, 67, 108},
+    {28, -2, 67, 108},
+    {35, -3, 67, 108}
+};
+
+static const char gQuadToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gQuadToInfo[] = {
+    {0, 8, 98, 1},
+    {3, 16, 98, 1},
+    {6, 12, 98, 1},
+    {9, 20, 98, 1}
+};
+
+static const char gRCubicToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRCubicToInfo[] = {
+    {0, 18, 18, 6}
+};
+
+static const char gRLineToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRLineToInfo[] = {
+    {0, 34, 18, 2}
+};
+
+static const char gRMoveToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRMoveToInfo[] = {
+    {0, 38, 18, 2}
+};
+
+static const char gRQuadToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRQuadToInfo[] = {
+    {0, 49, 18, 4}
+};
+
+static const char gRadialGradientStrings[] = 
+    "\0"
+    "center\0"
+    "radius"
+;
+
+static const SkMemberInfo gRadialGradientInfo[] = {
+    {0, 26, 18, 3},
+    {1, 48, 77, 2},
+    {8, 56, 98, 1}
+};
+
+static const char gRandomStrings[] = 
+    "blend\0"
+    "max\0"
+    "min\0"
+    "random\0"
+    "seed"
+;
+
+static const SkMemberInfo gDisplayRandomInfo[] = {
+    {0, 4, 98, 1},
+    {6, 12, 98, 1},
+    {10, 8, 98, 1},
+    {14, 1, 67, 98},
+    {21, -2, 67, 96}
+};
+
+static const char gRectToRectStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gRectToRectInfo[] = {
+    {0, 12, 91, 1},
+    {12, 8, 91, 1}
+};
+
+static const char gRectangleStrings[] = 
+    "bottom\0"
+    "height\0"
+    "left\0"
+    "needsRedraw\0"
+    "right\0"
+    "top\0"
+    "width"
+;
+
+static const SkMemberInfo gRectangleInfo[] = {
+    {0, 24, 98, 1},
+    {7, -1, 67, 98},
+    {14, 12, 98, 1},
+    {19, -2, 67, 26},
+    {31, 20, 98, 1},
+    {37, 16, 98, 1},
+    {41, -3, 67, 98}
+};
+
+static const char gRemoveStrings[] = 
+    "offset\0"
+    "where"
+;
+
+static const SkMemberInfo gRemoveInfo[] = {
+    {0, 8, 96, 1},
+    {7, 16, 37, 1}
+};
+
+static const char gReplaceStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gReplaceInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gRotateStrings[] = 
+    "center\0"
+    "degrees"
+;
+
+static const SkMemberInfo gRotateInfo[] = {
+    {0, 12, 77, 2},
+    {7, 8, 98, 1}
+};
+
+static const char gRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gRoundRectInfo[] = {
+    {0, 57, 18, 7},
+    {1, 32, 98, 1},
+    {4, 36, 98, 1}
+};
+
+static const char gS32Strings[] = 
+    "value"
+;
+
+static const SkMemberInfo gS32Info[] = {
+    {0, 4, 96, 1}
+};
+
+static const char gScalarStrings[] = 
+    "value"
+;
+
+static const SkMemberInfo gScalarInfo[] = {
+    {0, 4, 98, 1}
+};
+
+static const char gScaleStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gScaleInfo[] = {
+    {0, 16, 77, 2},
+    {7, 8, 98, 1},
+    {9, 12, 98, 1}
+};
+
+static const char gSetStrings[] = 
+    "begin\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "reset\0"
+    "target\0"
+    "to"
+;
+
+static const SkMemberInfo gSetInfo[] = {
+    {0, 4, 71, 1},
+    {6, 16, 71, 1},
+    {10, -1, 67, 26},
+    {18, 20, 108, 1},
+    {24, 24, 40, 1},
+    {32, -3, 67, 26},
+    {38, 36, 40, 1},
+    {45, 40, 40, 1}
+};
+
+static const char gShaderStrings[] = 
+    "matrix\0"
+    "tileMode"
+;
+
+static const SkMemberInfo gShaderInfo[] = {
+    {0, 8, 65, 1},
+    {7, 12, 116, 1}
+};
+
+static const char gSkewStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gSkewInfo[] = {
+    {0, 16, 77, 2},
+    {7, 8, 98, 1},
+    {9, 12, 98, 1}
+};
+
+static const char g3D_CameraStrings[] = 
+    "axis\0"
+    "hackHeight\0"
+    "hackWidth\0"
+    "location\0"
+    "observer\0"
+    "patch\0"
+    "zenith"
+;
+
+static const SkMemberInfo g3D_CameraInfo[] = {
+    {0, 24, 106, 3},
+    {5, 8, 98, 1},
+    {16, 4, 98, 1},
+    {26, 12, 106, 3},
+    {35, 48, 106, 3},
+    {44, 96, 105, 1},
+    {50, 36, 106, 3}
+};
+
+static const char g3D_PatchStrings[] = 
+    "origin\0"
+    "rotateDegrees\0"
+    "u\0"
+    "v"
+;
+
+static const SkMemberInfo g3D_PatchInfo[] = {
+    {0, 28, 106, 3},
+    {7, -1, 66, 98},
+    {21, 4, 106, 3},
+    {23, 16, 106, 3}
+};
+
+static const char gUnknown6Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown6Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gSnapshotStrings[] = 
+    "filename\0"
+    "quality\0"
+    "sequence\0"
+    "type"
+;
+
+static const SkMemberInfo gSnapshotInfo[] = {
+    {0, 4, 108, 1},
+    {9, 8, 98, 1},
+    {17, 12, 26, 1},
+    {26, 16, 20, 1}
+};
+
+static const char gStringStrings[] = 
+    "length\0"
+    "slice\0"
+    "value"
+;
+
+static const SkMemberInfo gStringInfo[] = {
+    {0, -1, 67, 96},
+    {7, -1, 66, 108},
+    {13, 4, 108, 1}
+};
+
+static const char gTextStrings[] = 
+    "length\0"
+    "text\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTextInfo[] = {
+    {0, -1, 67, 96},
+    {7, 12, 108, 1},
+    {12, 16, 98, 1},
+    {14, 20, 98, 1}
+};
+
+static const char gTextBoxStrings[] = 
+    "\0"
+    "mode\0"
+    "spacingAdd\0"
+    "spacingAlign\0"
+    "spacingMul\0"
+    "text"
+;
+
+static const SkMemberInfo gTextBoxInfo[] = {
+    {0, 57, 18, 7},
+    {1, 44, 113, 1},
+    {6, 40, 98, 1},
+    {17, 48, 112, 1},
+    {30, 36, 98, 1},
+    {41, 32, 108, 1}
+};
+
+static const char gTextOnPathStrings[] = 
+    "offset\0"
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextOnPathInfo[] = {
+    {0, 12, 98, 1},
+    {7, 16, 74, 1},
+    {12, 20, 110, 1}
+};
+
+static const char gTextToPathStrings[] = 
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextToPathInfo[] = {
+    {0, 4, 74, 1},
+    {5, 8, 110, 1}
+};
+
+static const char gTranslateStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTranslateInfo[] = {
+    {0, 8, 98, 1},
+    {2, 12, 98, 1}
+};
+
+static const char gTypedArrayStrings[] = 
+    "length\0"
+    "values"
+;
+
+static const SkMemberInfo gTypedArrayInfo[] = {
+    {0, -1, 67, 96},
+    {7, 4, 119, 0}
+};
+
+static const char gTypefaceStrings[] = 
+    "fontName"
+;
+
+static const SkMemberInfo gTypefaceInfo[] = {
+    {0, 8, 108, 1}
+};
+
+static const SkMemberInfo* const gInfoTables[] = {
+    gMathInfo,
+    gAddInfo,
+    gAddCircleInfo,
+    gUnknown1Info,
+    gAddOvalInfo,
+    gAddPathInfo,
+    gAddRectangleInfo,
+    gAddRoundRectInfo,
+    gUnknown2Info,
+    gAnimateFieldInfo,
+    gApplyInfo,
+    gUnknown3Info,
+    gDrawBitmapInfo,
+    gDrawBitmapShaderInfo,
+    gDrawBlurInfo,
+    gDisplayBoundsInfo,
+    gDrawClipInfo,
+    gDrawColorInfo,
+    gCubicToInfo,
+    gDashInfo,
+    gDataInfo,
+    gDiscreteInfo,
+    gDrawToInfo,
+    gDrawEmbossInfo,
+    gDisplayEventInfo,
+    gFromPathInfo,
+    gUnknown4Info,
+    gGInfo,
+    gHitClearInfo,
+    gHitTestInfo,
+    gImageInfo,
+    gIncludeInfo,
+    gInputInfo,
+    gLineInfo,
+    gLineToInfo,
+    gLinearGradientInfo,
+    gDrawMatrixInfo,
+    gMoveInfo,
+    gMoveToInfo,
+    gMovieInfo,
+    gOvalInfo,
+    gDrawPaintInfo,
+    gDrawPathInfo,
+    gUnknown5Info,
+    gDrawPointInfo,
+    gPolyToPolyInfo,
+    gPolygonInfo,
+    gPolylineInfo,
+    gPostInfo,
+    gQuadToInfo,
+    gRCubicToInfo,
+    gRLineToInfo,
+    gRMoveToInfo,
+    gRQuadToInfo,
+    gRadialGradientInfo,
+    gDisplayRandomInfo,
+    gRectToRectInfo,
+    gRectangleInfo,
+    gRemoveInfo,
+    gReplaceInfo,
+    gRotateInfo,
+    gRoundRectInfo,
+    gS32Info,
+    gScalarInfo,
+    gScaleInfo,
+    gSetInfo,
+    gShaderInfo,
+    gSkewInfo,
+    g3D_CameraInfo,
+    g3D_PatchInfo,
+    gUnknown6Info,
+    gSnapshotInfo,
+    gStringInfo,
+    gTextInfo,
+    gTextBoxInfo,
+    gTextOnPathInfo,
+    gTextToPathInfo,
+    gTranslateInfo,
+    gTypedArrayInfo,
+    gTypefaceInfo,
+};
+
+static const unsigned char gInfoCounts[] = {
+    26,4,4,1,1,2,5,3,13,1,13,2,6,3,2,2,2,8,6,
+    2,2,2,2,4,8,3,3,2,1,4,3,1,3,4,2,2,12,1,2,
+    1,1,25,3,3,2,2,1,1,6,4,1,1,1,1,3,5,2,7,2,
+    1,2,3,1,1,3,8,2,3,7,4,3,4,3,4,6,3,2,2,2,
+    1
+};
+
+static const unsigned char gTypeIDs[] = {
+    1, // Math
+    2, // Add
+    3, // AddCircle
+    4, // Unknown1
+    5, // AddOval
+    6, // AddPath
+    7, // AddRectangle
+    8, // AddRoundRect
+    10, // Unknown2
+    11, // AnimateField
+    12, // Apply
+    17, // Unknown3
+    19, // Bitmap
+    22, // BitmapShader
+    23, // Blur
+    25, // Bounds
+    29, // Clip
+    31, // Color
+    32, // CubicTo
+    33, // Dash
+    34, // Data
+    35, // Discrete
+    38, // DrawTo
+    41, // Emboss
+    42, // Event
+    48, // FromPath
+    51, // Unknown4
+    52, // G
+    53, // HitClear
+    54, // HitTest
+    55, // Image
+    56, // Include
+    57, // Input
+    59, // Line
+    60, // LineTo
+    61, // LinearGradient
+    65, // Matrix
+    68, // Move
+    69, // MoveTo
+    70, // Movie
+    72, // Oval
+    73, // Paint
+    74, // Path
+    77, // Unknown5
+    78, // Point
+    79, // PolyToPoly
+    80, // Polygon
+    81, // Polyline
+    82, // Post
+    83, // QuadTo
+    84, // RCubicTo
+    85, // RLineTo
+    86, // RMoveTo
+    87, // RQuadTo
+    88, // RadialGradient
+    89, // Random
+    90, // RectToRect
+    91, // Rectangle
+    92, // Remove
+    93, // Replace
+    94, // Rotate
+    95, // RoundRect
+    96, // S32
+    98, // Scalar
+    99, // Scale
+    101, // Set
+    102, // Shader
+    103, // Skew
+    104, // 3D_Camera
+    105, // 3D_Patch
+    106, // Unknown6
+    107, // Snapshot
+    108, // String
+    110, // Text
+    111, // TextBox
+    114, // TextOnPath
+    115, // TextToPath
+    117, // Translate
+    119, // TypedArray
+    120, // Typeface
+    
+};
+
+static const int kTypeIDs = 80;
+
+static const char* const gInfoNames[] = {
+    gMathStrings,
+    gAddStrings,
+    gAddCircleStrings,
+    gUnknown1Strings,
+    gAddOvalStrings,
+    gAddPathStrings,
+    gAddRectangleStrings,
+    gAddRoundRectStrings,
+    gUnknown2Strings,
+    gAnimateFieldStrings,
+    gApplyStrings,
+    gUnknown3Strings,
+    gBitmapStrings,
+    gBitmapShaderStrings,
+    gBlurStrings,
+    gBoundsStrings,
+    gClipStrings,
+    gColorStrings,
+    gCubicToStrings,
+    gDashStrings,
+    gDataStrings,
+    gDiscreteStrings,
+    gDrawToStrings,
+    gEmbossStrings,
+    gEventStrings,
+    gFromPathStrings,
+    gUnknown4Strings,
+    gGStrings,
+    gHitClearStrings,
+    gHitTestStrings,
+    gImageStrings,
+    gIncludeStrings,
+    gInputStrings,
+    gLineStrings,
+    gLineToStrings,
+    gLinearGradientStrings,
+    gMatrixStrings,
+    gMoveStrings,
+    gMoveToStrings,
+    gMovieStrings,
+    gOvalStrings,
+    gPaintStrings,
+    gPathStrings,
+    gUnknown5Strings,
+    gPointStrings,
+    gPolyToPolyStrings,
+    gPolygonStrings,
+    gPolylineStrings,
+    gPostStrings,
+    gQuadToStrings,
+    gRCubicToStrings,
+    gRLineToStrings,
+    gRMoveToStrings,
+    gRQuadToStrings,
+    gRadialGradientStrings,
+    gRandomStrings,
+    gRectToRectStrings,
+    gRectangleStrings,
+    gRemoveStrings,
+    gReplaceStrings,
+    gRotateStrings,
+    gRoundRectStrings,
+    gS32Strings,
+    gScalarStrings,
+    gScaleStrings,
+    gSetStrings,
+    gShaderStrings,
+    gSkewStrings,
+    g3D_CameraStrings,
+    g3D_PatchStrings,
+    gUnknown6Strings,
+    gSnapshotStrings,
+    gStringStrings,
+    gTextStrings,
+    gTextBoxStrings,
+    gTextOnPathStrings,
+    gTextToPathStrings,
+    gTranslateStrings,
+    gTypedArrayStrings,
+    gTypefaceStrings
+};
+#endif
+#endif
+
diff --git a/legacy/src/animator/SkDisplayAdd.cpp b/legacy/src/animator/SkDisplayAdd.cpp
new file mode 100644
index 0000000..1cf89a1
--- /dev/null
+++ b/legacy/src/animator/SkDisplayAdd.cpp
@@ -0,0 +1,246 @@
+
+/*
+ * 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 "SkDisplayAdd.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayList.h"
+#include "SkDrawable.h"
+#include "SkDrawGroup.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAdd::fInfo[] = {
+    SK_MEMBER(mode, AddMode),
+    SK_MEMBER(offset, Int),
+    SK_MEMBER(use, Drawable),
+    SK_MEMBER(where, Drawable)
+};
+
+#endif
+
+// start here;
+// add onEndElement to turn where string into f_Where
+// probably need new SkAnimateMaker::resolve flavor that takes
+// where="id", where="event-target" or not-specified
+// offset="#" (implements before, after, and index if no 'where')
+
+DEFINE_GET_MEMBER(SkAdd);
+
+SkAdd::SkAdd() : mode(kMode_indirect), 
+    offset(SK_MaxS32), use(NULL), where(NULL) {
+}
+
+SkDisplayable* SkAdd::deepCopy(SkAnimateMaker* maker) {
+    SkDrawable* saveUse = use;
+    SkDrawable* saveWhere = where;
+    use = NULL;
+    where = NULL;
+    SkAdd* copy = (SkAdd*) INHERITED::deepCopy(maker);
+    copy->use = use = saveUse;
+    copy->where = where = saveWhere;
+    return copy;
+}
+
+bool SkAdd::draw(SkAnimateMaker& maker) {
+    SkASSERT(use);
+    SkASSERT(use->isDrawable());
+    if (mode == kMode_indirect)
+        use->draw(maker);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkAdd::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (where)
+        SkDebugf("where=\"%s\" ", where->id);
+    if (mode == kMode_immediate)
+        SkDebugf("mode=\"immediate\" ");
+    SkDebugf(">\n");
+    SkDisplayList::fIndent += 4;
+    int save = SkDisplayList::fDumpIndex;
+    if (use)    //just in case
+        use->dump(maker);
+    SkDisplayList::fIndent -= 4;
+    SkDisplayList::fDumpIndex = save;
+    dumpEnd(maker);
+}
+#endif
+
+bool SkAdd::enable(SkAnimateMaker& maker ) {
+    SkDisplayTypes type = getType();
+    SkDisplayList& displayList = maker.fDisplayList;
+    SkTDDrawableArray* parentList = displayList.getDrawList();
+    if (type == SkType_Add) {
+        if (use == NULL) // not set in apply yet
+            return true;
+    }
+    bool skipAddToParent = true;
+    SkASSERT(type != SkType_Replace || where);
+    SkTDDrawableArray* grandList SK_INIT_TO_AVOID_WARNING;
+    SkGroup* parentGroup = NULL;
+    SkGroup* thisGroup = NULL;
+    int index = where ? displayList.findGroup(where, &parentList, &parentGroup,
+        &thisGroup, &grandList) : 0;
+    if (index < 0)
+        return true;
+    int max = parentList->count();
+    if (where == NULL && type == SkType_Move)
+        index = max;
+    if (offset != SK_MaxS32) {
+        index += offset;
+        if (index > max) {
+            maker.setErrorCode(SkDisplayXMLParserError::kIndexOutOfRange);
+            return true;    // caller should not add
+        }
+    }
+    if (offset < 0 && where == NULL)
+        index += max + 1;
+    switch (type) {
+        case SkType_Add:
+            if (offset == SK_MaxS32 && where == NULL) {
+                if (use->isDrawable()) {
+                    skipAddToParent = mode == kMode_immediate;
+                    if (skipAddToParent) {
+                        if (where == NULL) {
+                            SkTDDrawableArray* useParentList;
+                            index = displayList.findGroup(this, &useParentList, &parentGroup,
+                                &thisGroup, &grandList);
+                            if (index >= 0) {
+                                parentGroup->markCopySize(index);
+                                parentGroup->markCopySet(index);
+                                useParentList->begin()[index] = use;
+                                break;
+                            }                               
+                        }
+                        *parentList->append() = use;
+                    }
+                }
+                break;
+            } else {
+                if (thisGroup)
+                    thisGroup->markCopySize(index);
+                *parentList->insert(index) = use;
+                if (thisGroup)
+                    thisGroup->markCopySet(index);
+                if (use->isApply())
+                    ((SkApply*) use)->setEmbedded();
+            }
+            break;
+        case SkType_Move: {
+            int priorLocation = parentList->find(use);
+            if (priorLocation < 0)
+                break;
+            *parentList->insert(index) = use;
+            if (index < priorLocation)
+                priorLocation++;
+            parentList->remove(priorLocation);
+            } break;
+        case SkType_Remove: {
+            SkDisplayable* old = (*parentList)[index];
+            if (((SkRemove*)(this))->fDelete) {
+                delete old;
+                goto noHelperNeeded;
+            }
+            for (int inner = 0; inner < maker.fChildren.count(); inner++) {
+                SkDisplayable* child = maker.fChildren[inner];
+                if (child == old || child->contains(old))
+                    goto noHelperNeeded;
+            }
+            if (maker.fHelpers.find(old) < 0)
+                maker.helperAdd(old);
+noHelperNeeded:
+            parentList->remove(index);
+            } break;
+        case SkType_Replace:
+            if (thisGroup) {
+                thisGroup->markCopySize(index);
+                if (thisGroup->markedForDelete(index)) {
+                    SkDisplayable* old = (*parentList)[index];
+                    if (maker.fHelpers.find(old) < 0)
+                        maker.helperAdd(old);
+                }
+            }
+            (*parentList)[index] = use;
+            if (thisGroup)
+                thisGroup->markCopySet(index);
+            break;
+        default:
+            SkASSERT(0);
+    }
+    if (type == SkType_Remove)
+        return true;
+    if (use->hasEnable())
+        use->enable(maker);
+    return skipAddToParent; // append if indirect: *parentList->append() = this;
+}
+
+bool SkAdd::hasEnable() const {
+    return true;
+}
+
+void SkAdd::initialize() {
+    if (use)
+        use->initialize();
+}
+
+bool SkAdd::isDrawable() const {
+    return getType() == SkType_Add && mode == kMode_indirect && offset == SK_MaxS32 &&
+        where == NULL && use != NULL && use->isDrawable();
+}
+
+//SkDisplayable* SkAdd::resolveTarget(SkAnimateMaker& maker) {
+//  return use;
+//}
+
+
+bool SkClear::enable(SkAnimateMaker& maker ) {
+    SkDisplayList& displayList = maker.fDisplayList;
+    displayList.clear();
+    return true;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkMove::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkMove);
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRemove::fInfo[] = {
+    SK_MEMBER_ALIAS(delete, fDelete, Boolean),  // !!! experimental
+    SK_MEMBER(offset, Int),
+    SK_MEMBER(where, Drawable)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRemove);
+
+SkRemove::SkRemove() : fDelete(false) {
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkReplace::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkReplace);
+
diff --git a/legacy/src/animator/SkDisplayAdd.h b/legacy/src/animator/SkDisplayAdd.h
new file mode 100644
index 0000000..47948fb
--- /dev/null
+++ b/legacy/src/animator/SkDisplayAdd.h
@@ -0,0 +1,73 @@
+
+/*
+ * 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 SkDisplayAdd_DEFINED
+#define SkDisplayAdd_DEFINED
+
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+
+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
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+    virtual void initialize();
+    virtual bool isDrawable() const;
+protected:
+//  struct _A {
+        Mode mode;
+        int32_t offset;
+        SkDrawable* use;
+        SkDrawable* where;  // if NULL, offset becomes index
+//  } A;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+class SkClear : public SkDisplayable {
+    virtual bool enable(SkAnimateMaker& );
+};
+
+class SkMove : public SkAdd {
+    DECLARE_MEMBER_INFO(Move);
+private:
+    typedef SkAdd INHERITED;
+};
+
+class SkRemove : public SkAdd {
+    DECLARE_MEMBER_INFO(Remove);
+    SkRemove();
+protected:
+    SkBool fDelete;
+private:
+    friend class SkAdd;
+    typedef SkAdd INHERITED;
+};
+
+class SkReplace : public SkAdd {
+    DECLARE_MEMBER_INFO(Replace);
+private:
+    typedef SkAdd INHERITED;
+};
+
+#endif // SkDisplayAdd_DEFINED
+
+
diff --git a/legacy/src/animator/SkDisplayApply.cpp b/legacy/src/animator/SkDisplayApply.cpp
new file mode 100644
index 0000000..d54ee26
--- /dev/null
+++ b/legacy/src/animator/SkDisplayApply.cpp
@@ -0,0 +1,807 @@
+
+/*
+ * 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 "SkDisplayApply.h"
+#include "SkAnimateActive.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayType.h"
+#include "SkDrawGroup.h"
+#include "SkParse.h"
+#include "SkScript.h"
+#include "SkSystemEventTypes.h"
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+#include <ctype.h>
+
+enum SkApply_Properties {
+    SK_PROPERTY(animator),
+    SK_PROPERTY(step),
+    SK_PROPERTY(steps),
+    SK_PROPERTY(time)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+// if no attibutes, enclosed displayable is both scope & target
+// only if both scope & target are specified, or if target and enclosed displayable, are scope and target different
+const SkMemberInfo SkApply::fInfo[] = {
+    SK_MEMBER_PROPERTY(animator, Animate),
+    SK_MEMBER(begin, MSec),
+    SK_MEMBER(dontDraw, Boolean),
+    SK_MEMBER(dynamicScope, String),
+    SK_MEMBER(interval, MSec),  // recommended redraw interval
+    SK_MEMBER(mode, ApplyMode),
+#if 0
+    SK_MEMBER(pickup, Boolean),
+#endif
+    SK_MEMBER(restore, Boolean),
+    SK_MEMBER(scope, Drawable), // thing that scopes animation (unnamed enclosed displayable goes here)
+    SK_MEMBER_PROPERTY(step, Int),
+    SK_MEMBER_PROPERTY(steps, Int),
+    SK_MEMBER_PROPERTY(time, MSec),
+    SK_MEMBER(transition, ApplyTransition)
+};
+
+#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), 
+    fEnabled(false), fEnabling(false) {
+}
+
+SkApply::~SkApply() {
+    for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++)
+        delete *curPtr;
+    if (fDeleteScope)
+        delete scope;
+    // !!! caller must call maker.removeActive(fActive)
+    delete fActive;
+}
+
+void SkApply::activate(SkAnimateMaker& maker) {
+    if (fActive != NULL) {
+        if (fActive->fDrawIndex == 0 && fActive->fDrawMax == 0)
+            return; // if only one use, nothing more to do
+        if (restore == false)
+            return; // all share same state, regardless of instance number
+        bool save = fActive->initializeSave();
+        fActive->fixInterpolator(save);
+    } else {
+        fActive = new SkActive(*this, maker);
+        fActive->init();
+        maker.appendActive(fActive);
+        if (restore) {
+            fActive->initializeSave();
+            int animators = fAnimators.count();
+            for (int index = 0; index < animators; index++)
+                fActive->saveInterpolatorValues(index);
+        }
+    }
+}
+
+void SkApply::append(SkApply* apply) {
+    if (fActive == NULL)
+        return;
+    int oldCount = fActive->fAnimators.count();
+    fActive->append(apply);
+    if (restore) {
+        fActive->appendSave(oldCount);
+        int newCount = fActive->fAnimators.count();
+        for (int index = oldCount; index < newCount; index++)
+            fActive->saveInterpolatorValues(index);
+    }
+}
+
+void SkApply::applyValues(int animatorIndex, SkOperand* values, int count,
+     SkDisplayTypes valuesType, SkMSec time) 
+{
+    SkAnimateBase* animator = fActive->fAnimators[animatorIndex];
+    const SkMemberInfo * info = animator->fFieldInfo;
+    SkASSERT(animator);
+    SkASSERT(info != NULL);
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    SkDisplayable* target = getTarget(animator);
+    if (animator->hasExecute() || type == SkType_MemberFunction || type == SkType_MemberProperty) {
+        SkDisplayable* executor = animator->hasExecute() ? animator : target;
+        if (type != SkType_MemberProperty) {
+            SkTDArray<SkScriptValue> typedValues;
+            for (int index = 0; index < count; index++) {
+                SkScriptValue temp;
+                temp.fType = valuesType;
+                temp.fOperand = values[index];
+                *typedValues.append() = temp;
+            }
+            executor->executeFunction(target, info->functionIndex(), typedValues, info->getType(), NULL);
+        } else {
+            SkScriptValue scriptValue;
+            scriptValue.fOperand = values[0];
+            scriptValue.fType = info->getType();
+            target->setProperty(info->propertyIndex(), scriptValue);
+        }
+    } else {
+        SkTypedArray converted;
+        if (type == SkType_ARGB) {
+            if (count == 4) {
+                // !!! assert that it is SkType_Float ?
+                animator->packARGB(&values->fScalar, count, &converted);
+                values = converted.begin();
+                count = converted.count();
+            } else {
+                SkASSERT(count == 1);
+            }
+        }
+//      SkASSERT(type == SkType_ARGB || type == SkType_String ||info->isSettable());
+        if (type == SkType_String || type == SkType_DynamicString)
+            info->setString(target, values->fString);
+        else if (type == SkType_Drawable || type == SkType_Displayable)
+            target->setReference(info, values->fDisplayable);
+        else
+            info->setValue(target, values, count);
+    } 
+}
+
+bool SkApply::contains(SkDisplayable* child) {
+    for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++) {
+        if (*curPtr == child || (*curPtr)->contains(child))
+            return true;
+    }
+    return fDeleteScope && scope == child;
+}
+
+SkDisplayable* SkApply::deepCopy(SkAnimateMaker* maker) {
+    SkDrawable* saveScope = scope;
+    scope = NULL;
+    SkApply* result = (SkApply*) INHERITED::deepCopy(maker);
+    result->scope = scope = saveScope;
+    SkAnimateBase** end = fAnimators.end();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < end; animPtr++) {
+        SkAnimateBase* anim = (SkAnimateBase*) (*animPtr)->deepCopy(maker);
+        *result->fAnimators.append() = anim;
+        maker->helperAdd(anim);
+    }
+    return result;
+}
+
+void SkApply::disable() {
+    //!!! 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 
+    // instances are not affected.
+//  fEnabled = false;
+}
+
+bool SkApply::draw(SkAnimateMaker& maker) {
+    if (scope ==NULL)
+        return false;
+    if (scope->isApply() || scope->isDrawable() == false)
+        return false;
+    if (fEnabled == false)
+        enable(maker);
+    SkASSERT(scope);
+    activate(maker);
+    if (mode == kMode_immediate)
+        return fActive->draw();
+    bool result = interpolate(maker, maker.getInTime());
+    if (dontDraw == false) {
+//      if (scope->isDrawable())
+            result |= scope->draw(maker);
+    }
+    if (restore) {
+        for (int index = 0; index < fActive->fAnimators.count(); index++)
+            endSave(index);
+        fActive->advance();
+    }
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkApply::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (dynamicScope.isEmpty() == false)
+        SkDebugf("dynamicScope=\"%s\" ", dynamicScope.c_str());
+    if (dontDraw)
+        SkDebugf("dontDraw=\"true\" ");
+    if (begin != 0) //perhaps we want this no matter what?
+        SkDebugf("begin=\"%g\" ", (float) begin/1000.0f);   //is this correct?
+    if (interval != (SkMSec) -1)
+        SkDebugf("interval=\"%g\" ", (float) interval/1000.0f);
+    if (steps != -1)
+        SkDebugf("steps=\"%d\" ", steps);
+    if (restore)
+        SkDebugf("restore=\"true\" ");
+    if (transition == kTransition_reverse)
+        SkDebugf("transition=\"reverse\" ");
+    if (mode == kMode_immediate) {
+        SkDebugf("mode=\"immediate\" ");
+    }
+    else if (mode == kMode_create) {
+        SkDebugf("mode=\"create\" ");
+    }
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    int save = SkDisplayList::fDumpIndex;
+    if (scope) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        scope->dump(maker);
+    }
+    int index;
+//  if (fActive) {
+        for (index = 0; index < fAnimators.count(); index++) {
+            if (closedYet == false) {
+                SkDebugf(">\n");
+                closedYet = true;
+            }
+            SkAnimateBase* animator = fAnimators[index];
+            animator->dump(maker);
+//      }
+    }
+    SkDisplayList::fIndent -= 4;
+    SkDisplayList::fDumpIndex = save;
+    if (closedYet)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+}
+#endif
+
+bool SkApply::enable(SkAnimateMaker& maker) {
+    fEnabled = true;
+    bool initialized = fActive != NULL;
+    if (dynamicScope.size() > 0)
+        enableDynamic(maker);
+    if (maker.fError.hasError()) 
+        return false;
+    int animators = fAnimators.count();
+    int index;
+    for (index = 0; index < animators; index++) {
+        SkAnimateBase* animator = fAnimators[index];
+        animator->fStart = maker.fEnableTime;
+        animator->fResetPending = animator->fReset;
+    }
+    if (scope && scope->isApply())
+        ((SkApply*) scope)->setEmbedded();
+/*  if (mode == kMode_once) {
+        if (scope) {
+            activate(maker);
+            interpolate(maker, maker.fEnableTime);
+            inactivate(maker);
+        }
+        return true;
+    }*/
+    if ((mode == kMode_immediate || mode == kMode_create) && scope == NULL)
+        return false;   // !!! error?
+    bool enableMe = scope && (scope->hasEnable() || scope->isApply() || scope->isDrawable() == false);
+    if ((mode == kMode_immediate && enableMe) || mode == kMode_create)
+        activate(maker);    // for non-drawables like post, prime them here
+    if (mode == kMode_immediate && enableMe)
+        fActive->enable();
+    if (mode == kMode_create && scope != NULL) {
+        enableCreate(maker);
+        return true;
+    }
+    if (mode == kMode_immediate) {
+        return scope->isApply() || scope->isDrawable() == false;
+    }
+    refresh(maker);
+    SkDisplayList& displayList = maker.fDisplayList;
+    SkDrawable* drawable;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkString debugOut;
+    SkMSec time = maker.getAppTime();
+    debugOut.appendS32(time - maker.fDebugTimeBase);
+    debugOut.append(" apply enable id=");
+    debugOut.append(_id);
+    debugOut.append("; start=");
+    debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
+    SkDebugf("%s\n", debugOut.c_str());
+#endif
+    if (scope == NULL || scope->isApply() || scope->getType() == SkType_Movie || scope->isDrawable() == false) {
+        activate(maker);    // for non-drawables like post, prime them here
+        if (initialized) {
+            append(this);
+        }
+        fEnabling = true;
+        interpolate(maker, maker.fEnableTime);
+        fEnabling = false;
+        if (scope != NULL && dontDraw == false)
+            scope->enable(maker);
+        return true;
+    } else if (initialized && restore == false)
+        append(this);
+#if 0
+    bool wasActive = inactivate(maker); // start fresh
+    if (wasActive) {
+        activate(maker);
+        interpolate(maker, maker.fEnableTime);
+        return true;
+    }
+#endif
+//  start here;
+    // 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
+    if (fEmbedded) {
+        return false;   // already added to display list by embedder
+    }
+    drawable = (SkDrawable*) scope;
+    SkTDDrawableArray* parentList;
+    SkTDDrawableArray* grandList;
+    SkGroup* parentGroup;
+    SkGroup* thisGroup;
+    int old = displayList.findGroup(drawable, &parentList, &parentGroup, &thisGroup, &grandList);
+    if (old < 0)
+        goto append;
+    else if (fContainsScope) {
+        if ((*parentList)[old] != this || restore) {
+append:
+            if (parentGroup)
+                parentGroup->markCopySize(old);
+            if (parentList->count() < 10000) {
+                fAppended = true;
+                *parentList->append() = this;
+            } else
+                maker.setErrorCode(SkDisplayXMLParserError::kDisplayTreeTooDeep);
+            old = -1;
+        } else
+            reset();
+    } else {
+        SkASSERT(old < parentList->count());
+        if ((*parentList)[old]->isApply()) {
+            SkApply* apply = (SkApply*) (*parentList)[old];
+            if (apply != this && apply->fActive == NULL)
+                apply->activate(maker);
+            apply->append(this);
+            parentGroup = NULL;
+        } else {
+            if (parentGroup)
+                parentGroup->markCopySize(old);
+            SkDrawable** newApplyLocation = &(*parentList)[old];
+            SkGroup* pGroup;
+            int oldApply = displayList.findGroup(this, &parentList, &pGroup, &thisGroup, &grandList);
+            if (oldApply >= 0) {
+                (*parentList)[oldApply] = (SkDrawable*) SkDisplayType::CreateInstance(&maker, SkType_Apply);
+                parentGroup = NULL;
+                fDeleteScope = true;
+            }
+            *newApplyLocation = this;
+        }
+    }
+    if (parentGroup) {
+        parentGroup->markCopySet(old);
+        fDeleteScope = dynamicScope.size() == 0;
+    }
+    return true;
+}
+
+void SkApply::enableCreate(SkAnimateMaker& maker) {
+    SkString newID;
+    for (int step = 0; step <= steps; step++) {
+        fLastTime = step * SK_MSec1;
+        bool success = maker.computeID(scope, this, &newID);
+        if (success == false)
+            return;
+        if (maker.find(newID.c_str(), NULL))
+            continue;
+        SkApply* copy = (SkApply*) deepCopy(&maker); // work on copy of animator state
+        if (mode == kMode_create)
+            copy->mode = (Mode) -1;
+        SkDrawable* copyScope = copy->scope = (SkDrawable*) scope->deepCopy(&maker);
+        *fScopes.append() = copyScope;
+        if (copyScope->resolveIDs(maker, scope, this)) {
+            step = steps; // quit
+            goto next; // resolveIDs failed
+        }
+        if (newID.size() > 0) 
+            maker.setID(copyScope, newID);
+        if (copy->resolveIDs(maker, this, this)) { // fix up all fields, including target
+            step = steps; // quit
+            goto next; // resolveIDs failed
+        }
+        copy->activate(maker);
+        copy->interpolate(maker, step * SK_MSec1);
+        maker.removeActive(copy->fActive);
+    next:
+        delete copy;
+    }
+}
+
+void SkApply::enableDynamic(SkAnimateMaker& maker) {
+    SkASSERT(mode != kMode_create); // create + dynamic are not currently compatible
+    SkDisplayable* newScope;
+    bool success = SkAnimatorScript::EvaluateDisplayable(maker, this, dynamicScope.c_str(),
+        &newScope);
+    if (success && scope != newScope) {
+        SkTDDrawableArray* pList, * gList;
+        SkGroup* pGroup = NULL, * found = NULL;
+        int old = maker.fDisplayList.findGroup(scope, &pList, &pGroup, &found, &gList);
+        if (pList && old >= 0 && (*pList)[old]->isApply() && (*pList)[old] != this) {
+            if (fAppended == false) {
+                if (found != NULL) {
+                    SkDisplayable* oldChild = (*pList)[old];
+                    if (oldChild->isApply() && found->copySet(old)) {
+                        found->markCopyClear(old);
+    //                  delete oldChild;
+                    }
+                }
+                (*pList)[old] = scope;
+            } else
+                pList->remove(old);
+        }
+        scope = (SkDrawable*) newScope;
+        onEndElement(maker);
+    }
+    maker.removeActive(fActive);
+    delete fActive;
+    fActive = NULL;
+}
+
+void SkApply::endSave(int index) {
+    SkAnimateBase* animate = fActive->fAnimators[index];
+    const SkMemberInfo* info = animate->fFieldInfo;
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    if (type == SkType_MemberFunction)
+        return;
+    SkDisplayable* target = getTarget(animate);
+    size_t size = info->getSize(target);
+    int count = (int) (size / sizeof(SkScalar));
+    int activeIndex = fActive->fDrawIndex + index;
+    SkOperand* last = new SkOperand[count];
+    SkAutoTDelete<SkOperand> autoLast(last);
+    if (type != SkType_MemberProperty) {
+        info->getValue(target, last, count);
+        SkOperand* saveOperand = fActive->fSaveRestore[activeIndex];
+        if (saveOperand)
+            info->setValue(target, fActive->fSaveRestore[activeIndex], count);
+    } else {
+        SkScriptValue scriptValue;
+        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkASSERT(success == true);
+        last[0] = scriptValue.fOperand;
+        scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0];
+        target->setProperty(info->propertyIndex(), scriptValue);
+    }
+    SkOperand* save = fActive->fSaveRestore[activeIndex];
+    if (save)
+        memcpy(save, last, count * sizeof(SkOperand));
+}
+
+bool SkApply::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(step):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = fLastTime / SK_MSec1;
+            break;
+        case SK_PROPERTY(steps):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = steps;
+            break;
+        case SK_PROPERTY(time):
+            value->fType = SkType_MSec;
+            value->fOperand.fS32 = fLastTime;
+            break;
+        default:
+    //      SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+void SkApply::getStep(SkScriptValue* value) {
+    getProperty(SK_PROPERTY(step), value);
+}
+
+SkDrawable* SkApply::getTarget(SkAnimateBase* animate) {
+    if (animate->fTargetIsScope == false || mode != kMode_create)
+        return animate->fTarget;
+    return scope;
+}
+
+bool SkApply::hasDelayedAnimator() const {
+    SkAnimateBase** animEnd = fAnimators.end();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < animEnd; animPtr++) {
+        SkAnimateBase* animator = *animPtr;
+        if (animator->fDelayed) 
+            return true;
+    }
+    return false;
+}
+
+bool SkApply::hasEnable() const {
+    return true;
+}
+
+bool SkApply::inactivate(SkAnimateMaker& maker) {
+    if (fActive == NULL)
+        return false;
+    maker.removeActive(fActive);
+    delete fActive;
+    fActive = NULL;
+    return true;
+}
+
+#ifdef SK_DEBUG
+SkMSec lastTime = (SkMSec) -1;
+#endif
+
+bool SkApply::interpolate(SkAnimateMaker& maker, SkMSec rawTime) {
+    if (fActive == NULL)
+        return false;
+    bool result = false;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkMSec time = maker.getAppTime();
+    if (lastTime == (SkMSec) -1)
+        lastTime = rawTime - 1;
+    if (fActive != NULL && 
+        strcmp(id, "a3") == 0 && rawTime > lastTime) {
+        lastTime += 1000;
+        SkString debugOut;
+        debugOut.appendS32(time - maker.fDebugTimeBase);
+        debugOut.append(" apply id=");
+        debugOut.append(_id);
+        debugOut.append("; ");
+        debugOut.append(fActive->fAnimators[0]->_id);
+        debugOut.append("=");
+        debugOut.appendS32(rawTime - fActive->fState[0].fStartTime);
+        debugOut.append(")");
+        SkDebugf("%s\n", debugOut.c_str());
+    }
+#endif
+    fActive->start();
+    if (restore)
+        fActive->initializeSave();
+    int animators = fActive->fAnimators.count();
+    for (int inner = 0; inner < animators; inner++) {
+        SkAnimateBase* animate = fActive->fAnimators[inner];
+        if (animate->fChanged) {
+            animate->fChanged = false;
+            animate->fStart = rawTime;
+    //      SkTypedArray values;
+    //      int count = animate->fValues.count();
+    //      values.setCount(count);
+    //      memcpy(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count);
+            animate->onEndElement(maker);
+    //      if (memcmp(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count) != 0) {
+                fActive->append(this);
+                fActive->start();
+    //      }
+        }
+        SkMSec time = fActive->getTime(rawTime, inner);
+        SkActive::SkState& state = fActive->fState[inner];
+        if (SkMSec_LT(rawTime, state.fStartTime)) {
+            if (fEnabling) {
+                animate->fDelayed = true;
+                maker.delayEnable(this, state.fStartTime);
+            }
+            continue;
+        } else
+            animate->fDelayed = false;
+        SkMSec innerTime = fLastTime = state.getRelativeTime(time);
+        if (restore) 
+            fActive->restoreInterpolatorValues(inner);
+        if (animate->fReset) {
+            if (transition != SkApply::kTransition_reverse) {
+                if (SkMSec_LT(state.fBegin + state.fDuration, innerTime)) {
+                    if (animate->fResetPending) {
+                        innerTime = 0;
+                        animate->fResetPending = false;
+                    } else
+                        continue;
+                }
+            } else if (innerTime == 0) {
+                    if (animate->fResetPending) {
+                        innerTime = state.fBegin + state.fDuration;
+                        animate->fResetPending = false;
+                    } else
+                        continue;
+            }
+        }
+        int count = animate->components();
+        SkAutoSTMalloc<16, SkOperand> values(count);
+        SkInterpolatorBase::Result interpResult = fActive->fInterpolators[inner]->timeToValues(
+            innerTime, values.get());
+        result |= (interpResult != SkInterpolatorBase::kFreezeEnd_Result);
+        if (((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result) ||
+                (transition == SkApply::kTransition_reverse && fLastTime == 0)) && state.fUnpostedEndEvent) {
+//          SkDEBUGF(("interpolate: post on end\n"));
+            state.fUnpostedEndEvent = false;
+            maker.postOnEnd(animate, state.fBegin + state.fDuration);
+            maker.fAdjustedStart = 0;    // !!! left over from synchronizing animation days, undoubtably out of date (and broken)
+        }
+        if (animate->formula.size() > 0) {
+            if (fLastTime > animate->dur)
+                fLastTime = animate->dur;
+            SkTypedArray formulaValues;
+            formulaValues.setCount(count);
+            bool success = animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL, 
+                animate->getValuesType(), animate->formula);
+            SkASSERT(success);
+            if (restore)
+                save(inner); // save existing value
+            applyValues(inner, formulaValues.begin(), count, animate->getValuesType(), innerTime);
+        } else {
+            if (restore)
+                save(inner); // save existing value
+            applyValues(inner, values.get(), count, animate->getValuesType(), innerTime);
+        }
+    }
+    return result;
+}
+
+void SkApply::initialize() {
+    if (scope == NULL)
+        return;
+    if (scope->isApply() || scope->isDrawable() == false)
+        return;
+    scope->initialize();
+}
+
+void SkApply::onEndElement(SkAnimateMaker& maker) 
+{
+    SkDrawable* scopePtr = scope;
+    while (scopePtr && scopePtr->isApply()) {
+        SkApply* scopedApply = (SkApply*) scopePtr;
+        if (scopedApply->scope == this) {
+            maker.setErrorCode(SkDisplayXMLParserError::kApplyScopesItself);
+            return;
+        }
+        scopePtr = scopedApply->scope;
+    }
+    if (mode == kMode_create)
+        return;
+    if (scope != NULL && steps >= 0 && scope->isApply() == false && scope->isDrawable())
+        scope->setSteps(steps);
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) {
+        SkAnimateBase* anim = *animPtr;
+        //for reusing apply statements with dynamic scope
+        if (anim->fTarget == NULL || anim->fTargetIsScope) {
+            anim->fTargetIsScope = true;
+            if (scope)
+                anim->fTarget = scope;
+            else
+                anim->setTarget(maker);
+            anim->onEndElement(maker);  // allows animate->fFieldInfo to be set
+        }
+        if (scope != NULL && steps >= 0 && anim->fTarget != scope && anim->fTarget->isDrawable())
+            anim->fTarget->setSteps(steps);
+    }
+}
+
+const SkMemberInfo* SkApply::preferredChild(SkDisplayTypes type) {
+    SkASSERT(SkDisplayType::IsAnimate(type) == false);
+    fContainsScope = true;
+    return getMember("scope"); // !!! cwap! need to refer to member through enum like kScope instead
+}
+
+void SkApply::refresh(SkAnimateMaker& maker) {
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) {
+        SkAnimateBase* animate = *animPtr;
+        animate->onEndElement(maker);
+    }
+    if (fActive)
+        fActive->resetInterpolators();
+}
+
+void SkApply::reset() {
+    if (fActive)
+        fActive->resetState();
+}
+
+bool SkApply::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) { //   replace to/formula strings in animators of the form xxx.step with the step value, if xxx.step is in scope
+    if (resolveField(maker, apply, &dynamicScope) == false)
+        return true;    // failed
+    SkAnimateBase** endPtr = fAnimators.end();
+    SkAnimateBase** origPtr = ((SkApply*) original)->fAnimators.begin();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < endPtr; ) {
+        SkAnimateBase* animator = *animPtr++;
+        maker.resolveID(animator, *origPtr++);
+        if (resolveField(maker, this, &animator->target) == false)
+            return true;
+        if (resolveField(maker, this, &animator->from) == false)
+            return true;
+        if (resolveField(maker, this, &animator->to) == false)
+            return true;
+        if (resolveField(maker, this, &animator->formula) == false)
+            return true;
+    }
+//  setEmbedded();
+    onEndElement(maker);
+    return false; // succeeded
+}
+
+bool SkApply::resolveField(SkAnimateMaker& maker, SkDisplayable* parent, SkString* str) {
+    const char* script = str->c_str();
+    if (str->startsWith("#string:") == false)
+        return true;
+    script += sizeof("#string:") - 1;
+    return SkAnimatorScript::EvaluateString(maker, this, parent, script, str);
+}
+
+void SkApply::save(int index) {
+    SkAnimateBase* animate = fActive->fAnimators[index];
+    const SkMemberInfo * info = animate->fFieldInfo;
+    SkDisplayable* target = getTarget(animate);
+//  if (animate->hasExecute())
+//      info = animate->getResolvedInfo();
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    if (type == SkType_MemberFunction)
+        return; // nothing to save
+    size_t size = info->getSize(target);
+    int count = (int) (size / sizeof(SkScalar));
+    bool useLast = true;
+// !!! this all may be unneeded, at least in the dynamic case ??
+    int activeIndex = fActive->fDrawIndex + index;
+    SkTDOperandArray last;
+    if (fActive->fSaveRestore[activeIndex] == NULL) {
+        fActive->fSaveRestore[activeIndex] = new SkOperand[count];
+        useLast = false;
+    } else {
+        last.setCount(count);
+        memcpy(last.begin(), fActive->fSaveRestore[activeIndex], count * sizeof(SkOperand));
+    }
+    if (type != SkType_MemberProperty) {
+        info->getValue(target, fActive->fSaveRestore[activeIndex], count);
+        if (useLast)
+            info->setValue(target, last.begin(), count);
+    } else {
+        SkScriptValue scriptValue;
+        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkASSERT(success == true);
+        SkASSERT(scriptValue.fType == SkType_Float);
+        fActive->fSaveRestore[activeIndex][0] = scriptValue.fOperand;
+        if (useLast) {
+            SkScriptValue scriptValue;
+            scriptValue.fType = type;
+            scriptValue.fOperand = last[0];
+            target->setProperty(info->propertyIndex(), scriptValue);
+        }
+    }
+// !!!  end of unneeded
+}
+
+bool SkApply::setProperty(int index, SkScriptValue& scriptValue) {
+    switch (index) {
+        case SK_PROPERTY(animator): {
+            SkAnimateBase* animate = (SkAnimateBase*) scriptValue.fOperand.fDisplayable;
+            SkASSERT(animate->isAnimate());
+            *fAnimators.append() = animate;
+            return true; 
+        }
+        case SK_PROPERTY(steps):
+            steps = scriptValue.fOperand.fS32;
+            if (fActive)
+                fActive->setSteps(steps);
+            return true;
+    }
+    return false;
+}
+
+void SkApply::setSteps(int _steps) { 
+    steps = _steps; 
+}
+
+#ifdef SK_DEBUG
+void SkApply::validate() {
+    if (fActive)
+        fActive->validate();
+}
+#endif
+
+
+
diff --git a/legacy/src/animator/SkDisplayApply.h b/legacy/src/animator/SkDisplayApply.h
new file mode 100644
index 0000000..018b894
--- /dev/null
+++ b/legacy/src/animator/SkDisplayApply.h
@@ -0,0 +1,108 @@
+
+/*
+ * 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 SkDisplayApply_DEFINED
+#define SkDisplayApply_DEFINED
+
+#include "SkAnimateBase.h"
+#include "SkDrawable.h"
+#include "SkIntArray.h"
+
+class SkActive;
+
+class SkApply : public SkDrawable {
+    DECLARE_MEMBER_INFO(Apply);
+public:
+
+    SkApply();
+    virtual ~SkApply();
+
+    enum Transition {
+        kTransition_normal,
+        kTransition_reverse
+    };
+    
+    enum Mode {
+        kMode_create,
+        kMode_immediate,
+        //kMode_once
+    };
+    void activate(SkAnimateMaker& );
+    void append(SkApply* apply);
+    void appendActive(SkActive* );
+    void applyValues(int animatorIndex, SkOperand* values, int count,
+        SkDisplayTypes , SkMSec time);
+    virtual bool contains(SkDisplayable*);
+//  void createActive(SkAnimateMaker& );
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    void disable();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    void enableCreate(SkAnimateMaker& );
+    void enableDynamic(SkAnimateMaker& );
+    void endSave(int index);
+    Mode getMode() { return mode; }
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    SkDrawable* getScope() { return scope; }
+    void getStep(SkScriptValue* );
+    SkDrawable* getTarget(SkAnimateBase* );
+    bool hasDelayedAnimator() const;
+    virtual bool hasEnable() const;
+    bool inactivate(SkAnimateMaker& maker);
+    virtual void initialize(); 
+    bool interpolate(SkAnimateMaker& , SkMSec time);
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+    void refresh(SkAnimateMaker& );
+    void reset();
+    virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* );
+    bool resolveField(SkAnimateMaker& , SkDisplayable* parent, SkString* str);
+    void save(int index);
+    void setEmbedded() { fEmbedded = true; }
+    virtual bool setProperty(int index, SkScriptValue& );
+    virtual void setSteps(int _steps);
+//  virtual void setTime(SkMSec time);
+#ifdef SK_DEBUG
+    virtual void validate();
+#endif
+private:
+    SkMSec begin;
+    SkBool dontDraw;
+    SkString dynamicScope;
+    SkMSec interval;
+    Mode mode;
+#if 0
+    SkBool pickup;
+#endif
+    SkBool restore;
+    SkDrawable* scope;
+    int32_t steps;
+    Transition transition;
+    SkActive* fActive;
+    SkTDAnimateArray fAnimators;
+//  SkDrawable* fCurrentScope;
+    SkMSec fLastTime;   // used only to return script property time
+    SkTDDrawableArray fScopes;
+    SkBool fAppended : 1;
+    SkBool fContainsScope : 1;
+    SkBool fDeleteScope : 1;
+    SkBool fEmbedded : 1;
+    SkBool fEnabled : 1;
+    SkBool fEnabling : 1; // set if calling interpolate from enable
+    friend class SkActive;
+    friend class SkDisplayList;
+    typedef SkDrawable INHERITED;
+};
+
+#endif // SkDisplayApply_DEFINED
+
+
diff --git a/legacy/src/animator/SkDisplayBounds.cpp b/legacy/src/animator/SkDisplayBounds.cpp
new file mode 100644
index 0000000..69ec47e
--- /dev/null
+++ b/legacy/src/animator/SkDisplayBounds.cpp
@@ -0,0 +1,46 @@
+
+/*
+ * 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 "SkDisplayBounds.h"
+#include "SkAnimateMaker.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayBounds::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(inval, Boolean)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayBounds);
+
+SkDisplayBounds::SkDisplayBounds() : inval(false) {
+}
+
+bool SkDisplayBounds::draw(SkAnimateMaker& maker) {
+    maker.fDisplayList.fUnionBounds = SkToBool(inval);
+    maker.fDisplayList.fDrawBounds = false;
+    fBounds.setEmpty();
+    bool result = INHERITED::draw(maker);
+    maker.fDisplayList.fUnionBounds = false;
+    maker.fDisplayList.fDrawBounds = true;
+    if (inval && fBounds.isEmpty() == false) {
+        SkIRect& rect = maker.fDisplayList.fInvalBounds;
+        maker.fDisplayList.fHasUnion = true;
+        if (rect.isEmpty())
+            rect = fBounds;
+        else 
+            rect.join(fBounds);
+    }
+    return result;
+}
+
+
+
diff --git a/legacy/src/animator/SkDisplayBounds.h b/legacy/src/animator/SkDisplayBounds.h
new file mode 100644
index 0000000..bc3a987
--- /dev/null
+++ b/legacy/src/animator/SkDisplayBounds.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 SkDisplayBounds_DEFINED
+#define SkDisplayBounds_DEFINED
+
+#include "SkDrawRectangle.h"
+
+class SkDisplayBounds : public SkDrawRect {
+    DECLARE_DISPLAY_MEMBER_INFO(Bounds);
+    SkDisplayBounds();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkBool inval;
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDisplayBounds_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayEvent.cpp b/legacy/src/animator/SkDisplayEvent.cpp
new file mode 100644
index 0000000..7d2e786
--- /dev/null
+++ b/legacy/src/animator/SkDisplayEvent.cpp
@@ -0,0 +1,330 @@
+
+/*
+ * 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 "SkDisplayEvent.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayInput.h"
+#include "SkDisplayList.h"
+#ifdef SK_DEBUG
+#include "SkDump.h"
+#endif
+#include "SkEvent.h"
+#include "SkDisplayInput.h"
+#include "SkKey.h"
+#include "SkMetaData.h"
+#include "SkScript.h"
+#include "SkUtils.h"
+
+enum SkDisplayEvent_Properties {
+    SK_PROPERTY(key),
+    SK_PROPERTY(keys)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayEvent::fInfo[] = {
+    SK_MEMBER(code, EventCode),
+    SK_MEMBER(disable, Boolean),
+    SK_MEMBER_PROPERTY(key, String), // a single key (also last key pressed)
+    SK_MEMBER_PROPERTY(keys, String), // a single key or dash-delimited range of keys
+    SK_MEMBER(kind, EventKind),
+    SK_MEMBER(target, String),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayEvent);
+
+SkDisplayEvent::SkDisplayEvent() : code((SkKey) -1), disable(false),
+    kind(kUser), x(0), y(0), fLastCode((SkKey) -1), fMax((SkKey) -1), fTarget(NULL) {
+}
+
+SkDisplayEvent::~SkDisplayEvent() {
+    deleteMembers();
+}
+
+bool SkDisplayEvent::add(SkAnimateMaker& , SkDisplayable* child) { 
+    *fChildren.append() = child; 
+    return true; 
+}
+
+bool SkDisplayEvent::contains(SkDisplayable* match) {
+    for (int index = 0; index < fChildren.count(); index++) {
+        if (fChildren[index] == match || fChildren[index]->contains(match))
+            return true;
+    }
+    return false;
+}
+
+SkDisplayable* SkDisplayEvent::contains(const SkString& match) {
+    for (int index = 0; index < fChildren.count(); index++) {
+        SkDisplayable* child = fChildren[index];
+        if (child->contains(match))
+            return child;
+    }
+    return NULL;
+}
+
+void SkDisplayEvent::deleteMembers() {
+    for (int index = 0; index < fChildren.count(); index++) {
+        SkDisplayable* evt = fChildren[index];
+        delete evt;
+    }
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayEvent::dumpEvent(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkString str;
+    SkDump::GetEnumString(SkType_EventKind, kind, &str);
+    SkDebugf("kind=\"%s\" ", str.c_str());
+    if (kind == SkDisplayEvent::kKeyPress || kind == SkDisplayEvent::kKeyPressUp) {
+        if (code >= 0)
+            SkDump::GetEnumString(SkType_EventCode, code, &str);
+        else
+            str.set("none");
+        SkDebugf("code=\"%s\" ", str.c_str());
+    }
+    if (kind == SkDisplayEvent::kKeyChar) {
+        if (fMax != (SkKey) -1 && fMax != code)
+            SkDebugf("keys=\"%c - %c\" ", code, fMax);
+        else
+            SkDebugf("key=\"%c\" ", code);
+    }
+    if (fTarget != NULL) {
+        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\" ");
+    SkDebugf("/>\n");
+}
+#endif
+
+bool SkDisplayEvent::enableEvent(SkAnimateMaker& maker) 
+{
+    maker.fActiveEvent = this;
+    if (fChildren.count() == 0)
+        return false;
+    if (disable)
+        return false;
+#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpEvents) {
+        SkDebugf("enable: ");
+        dumpEvent(&maker);
+    }
+#endif
+    SkDisplayList& displayList = maker.fDisplayList;
+    for (int index = 0; index < fChildren.count(); index++) {
+        SkDisplayable* displayable = fChildren[index];
+        if (displayable->isGroup()) {
+            SkTDDrawableArray* parentList = displayList.getDrawList();
+            *parentList->append() = (SkDrawable*) displayable;  // make it findable before children are enabled
+        }
+        if (displayable->enable(maker))
+            continue;
+        if (maker.hasError()) 
+            return true;
+        if (displayable->isDrawable() == false)
+            return true;    // error
+        SkDrawable* drawable = (SkDrawable*) displayable;
+        SkTDDrawableArray* parentList = displayList.getDrawList();
+        *parentList->append() = drawable;
+    }
+    return false;
+}
+
+bool SkDisplayEvent::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(key):
+        case SK_PROPERTY(keys): {
+            value->fType = SkType_String;
+            char scratch[8];
+            SkKey convert = index == SK_PROPERTY(keys) ? code : fLastCode;
+            size_t size = convert > 0 ? SkUTF8_FromUnichar(convert, scratch) : 0;
+            fKeyString.set(scratch, size);
+            value->fOperand.fString = &fKeyString;
+            if (index != SK_PROPERTY(keys) || fMax == (SkKey) -1 || fMax == code)
+                break;
+            value->fOperand.fString->append("-");
+            size = SkUTF8_FromUnichar(fMax, scratch);
+            value->fOperand.fString->append(scratch, size);
+            } break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+void SkDisplayEvent::onEndElement(SkAnimateMaker& maker)
+{
+    if (kind == kUser)
+        return;
+    maker.fEvents.addEvent(this);
+    if (kind == kOnEnd) {
+        bool found = maker.find(target.c_str(), &fTarget);
+        SkASSERT(found);
+        SkASSERT(fTarget && fTarget->isAnimate());
+        SkAnimateBase* animate = (SkAnimateBase*) fTarget;
+        animate->setHasEndEvent();
+    }
+}
+
+void SkDisplayEvent::populateInput(SkAnimateMaker& maker, const SkEvent& fEvent) {
+    const SkMetaData& meta = fEvent.getMetaData();
+    SkMetaData::Iter iter(meta);
+    SkMetaData::Type    type;
+    int number;
+    const char* name;
+    while ((name = iter.next(&type, &number)) != NULL) {
+        if (name[0] == '\0')
+            continue;
+        SkDisplayable* displayable;
+        SkInput* input;
+        for (int index = 0; index < fChildren.count(); index++) {
+            displayable = fChildren[index];
+            if (displayable->getType() != SkType_Input)
+                continue;
+            input = (SkInput*) displayable;
+            if (input->name.equals(name))
+                goto found;
+        }
+        if (!maker.find(name, &displayable) || displayable->getType() != SkType_Input)
+            continue;
+        input = (SkInput*) displayable;
+    found:
+        switch (type) {
+            case SkMetaData::kS32_Type:
+                meta.findS32(name, &input->fInt);
+                break;
+            case SkMetaData::kScalar_Type:
+                meta.findScalar(name, &input->fFloat);
+                break;
+            case SkMetaData::kPtr_Type:
+                SkASSERT(0);
+                break; // !!! not handled for now
+            case SkMetaData::kString_Type:
+                input->string.set(meta.findString(name));
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+    // re-evaluate all animators that may have built their values from input strings
+    for (SkDisplayable** childPtr = fChildren.begin(); childPtr < fChildren.end(); childPtr++) {
+        SkDisplayable* displayable = *childPtr;
+        if (displayable->isApply() == false)
+            continue;
+        SkApply* apply = (SkApply*) displayable;
+        apply->refresh(maker);
+    }
+}
+
+bool SkDisplayEvent::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(index == SK_PROPERTY(key) || index == SK_PROPERTY(keys));
+    SkASSERT(value.fType == SkType_String);
+    SkString* string = value.fOperand.fString;
+    const char* chars = string->c_str();
+    int count = SkUTF8_CountUnichars(chars);
+    SkASSERT(count >= 1);
+    code = (SkKey) SkUTF8_NextUnichar(&chars);
+    fMax = code;
+    SkASSERT(count == 1 || index == SK_PROPERTY(keys));
+    if (--count > 0) {
+        SkASSERT(*chars == '-');
+        chars++;
+        fMax = (SkKey) SkUTF8_NextUnichar(&chars);
+        SkASSERT(fMax >= code);
+    }
+    return true;
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+
+#include "SkMetaData.h"
+#include "SkParse.h"
+#include "SkTextBox.h"
+#include "SkXMLWriter.h"
+
+void SkMetaData::setPtr(char const*, void*, PtrProc ) {}
+void SkMetaData::setS32(char const*, int ) {}
+bool SkEventSink::doEvent(SkEvent const& ) { return false; }
+bool SkXMLParser::parse(SkStream& ) { return false; }
+SkXMLParserError::SkXMLParserError( ) {}
+void SkEvent::setType(char const*, size_t ) {}
+void SkEvent::postTime(SkMSec) {}
+SkEvent::SkEvent(char const*, SkEventSinkID) {}
+SkEvent::SkEvent(SkEvent const&) {}
+SkEvent::SkEvent( ) {}
+SkEvent::~SkEvent( ) {}
+bool SkEventSink::onQuery(SkEvent* ) { return false; }
+SkEventSink::SkEventSink( ) {}
+SkEventSink::~SkEventSink( ) {}
+bool SkXMLParser::parse(char const*, size_t ) { return false; }
+bool SkXMLParser::parse(SkDOM const&, SkDOMNode const* ) { return false; }
+//void SkParse::UnitTest( ) {}
+const char* SkMetaData::findString(char const* ) const {return 0;}
+bool SkMetaData::findPtr(char const*, void**, PtrProc* ) const {return false;}
+bool SkMetaData::findS32(char const*, int* ) const {return false;}
+bool SkEvent::isType(char const*, size_t ) const { return false; }
+void SkMetaData::setString(char const*, char const* ) {}
+const char* SkParse::FindNamedColor(char const*, size_t, SkColor* ) {return false; }
+const char* SkMetaData::Iter::next(SkMetaData::Type*, int* ) { return false; }
+SkMetaData::Iter::Iter(SkMetaData const& ) {}
+bool SkMetaData::findScalar(char const*, SkScalar* ) const {return false;}
+void SkMetaData::reset( ) {}
+void SkEvent::setType(SkString const& ) {}
+bool SkMetaData::findBool(char const*, bool* ) const {return false;}
+void SkEvent::getType(SkString*) const {}
+bool SkXMLParser::endElement(char const* ) { return false; }
+bool SkXMLParser::addAttribute(char const*, char const* ) { return false;}
+bool SkXMLParser::startElement(char const* ) { return false;}
+bool SkXMLParser::text(char const*, int ) { return false;}
+bool SkXMLParser::onText(char const*, int ) { return false;}
+SkXMLParser::SkXMLParser(SkXMLParserError* ) {}
+SkXMLParser::~SkXMLParser( ) {}
+SkXMLParserError::~SkXMLParserError( ) {}
+void SkXMLParserError::getErrorString(SkString*) const {}
+void SkTextBox::setSpacing(SkScalar, SkScalar ) {}
+void SkTextBox::setSpacingAlign(SkTextBox::SpacingAlign ) {}
+void SkTextBox::draw(SkCanvas*, char const*, size_t, SkPaint const& ) {}
+void SkTextBox::setBox(SkRect const& ) {}
+void SkTextBox::setMode(SkTextBox::Mode ) {}
+SkTextBox::SkTextBox( ) {}
+void SkMetaData::setScalar(char const*, SkScalar ) {}
+const char* SkParse::FindScalar(char const*, SkScalar* ) {return 0; }
+const char* SkParse::FindScalars(char const*, SkScalar*, int ) {return 0; }
+const char* SkParse::FindHex(char const*, unsigned int* ) {return 0; }
+const char* SkParse::FindS32(char const*, int* ) {return 0; }
+void SkXMLWriter::addAttribute(char const*, char const* ) {}
+void SkXMLWriter::startElement(char const* ) {}
+void SkXMLWriter::doEnd(SkXMLWriter::Elem* ) {}
+SkXMLWriter::Elem* SkXMLWriter::getEnd( ) { return 0; }
+bool SkXMLWriter::doStart(char const*, size_t ) { return false; }
+SkXMLWriter::SkXMLWriter(bool ) {}
+SkXMLWriter::~SkXMLWriter( ) {}
+SkMetaData::SkMetaData() {}
+SkMetaData::~SkMetaData() {}
+bool SkEventSink::onEvent(SkEvent const&) {return false;}
+bool SkXMLParser::onEndElement(char const*) {return false;}
+bool SkXMLParser::onAddAttribute(char const*, char const*) {return false;}
+bool SkXMLParser::onStartElement(char const*) {return false;}
+void SkXMLWriter::writeHeader() {}
+
+#endif
diff --git a/legacy/src/animator/SkDisplayEvent.h b/legacy/src/animator/SkDisplayEvent.h
new file mode 100644
index 0000000..8b541fc
--- /dev/null
+++ b/legacy/src/animator/SkDisplayEvent.h
@@ -0,0 +1,67 @@
+
+/*
+ * 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 SkDisplayEvent_DEFINED
+#define SkDisplayEvent_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkIntArray.h"
+#include "SkKey.h"
+
+class SkEvent;
+
+class SkDisplayEvent : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Event);
+    enum Kind {
+        kNo_kind,
+        kKeyChar,
+        kKeyPress,
+        kKeyPressUp,    //i assume the order here is intended to match with skanimatorscript.cpp
+        kMouseDown,
+        kMouseDrag,
+        kMouseMove,
+        kMouseUp,
+        kOnEnd,
+        kOnload,
+        kUser
+    };
+    SkDisplayEvent();
+    virtual ~SkDisplayEvent();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool contains(SkDisplayable*);
+    virtual SkDisplayable* contains(const SkString& );
+#ifdef SK_DEBUG
+    void dumpEvent(SkAnimateMaker* );
+#endif
+    bool enableEvent(SkAnimateMaker& );
+    virtual bool getProperty(int index, SkScriptValue* ) const;
+    virtual void onEndElement(SkAnimateMaker& maker);
+    void populateInput(SkAnimateMaker& , const SkEvent& fEvent);
+    virtual bool setProperty(int index, SkScriptValue& );
+protected:
+    SkKey code;
+    SkBool disable;
+    Kind kind;
+    SkString target;
+    SkScalar x;
+    SkScalar y;
+    SkTDDisplayableArray fChildren;
+    mutable SkString fKeyString;
+    SkKey fLastCode; // last key to trigger this event
+    SkKey fMax; // if the code expresses a range
+    SkDisplayable* fTarget; // used by onEnd
+private:
+    void deleteMembers();
+    friend class SkEvents;
+    typedef SkDisplayable INHERITED;
+};
+
+#endif // SkDisplayEvent_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayEvents.cpp b/legacy/src/animator/SkDisplayEvents.cpp
new file mode 100644
index 0000000..38f82a0
--- /dev/null
+++ b/legacy/src/animator/SkDisplayEvents.cpp
@@ -0,0 +1,113 @@
+
+/*
+ * 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 "SkDisplayEvents.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimator.h"
+#include "SkDisplayEvent.h"
+#include "SkDisplayMovie.h"
+#include "SkDrawable.h"
+#ifdef SK_DEBUG
+#include "SkDump.h"
+#endif
+
+SkEventState::SkEventState() : fCode(0), fDisable(false), fDisplayable(0), fX(0), fY(0) {
+}
+
+SkEvents::SkEvents() {
+}
+
+SkEvents::~SkEvents() {
+}
+
+bool SkEvents::doEvent(SkAnimateMaker& maker, SkDisplayEvent::Kind kind, SkEventState* state) {
+/*#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpEvents) {
+        SkDebugf("doEvent: ");
+        SkString str;
+        SkDump::GetEnumString(SkType_EventKind, kind, &str);
+        SkDebugf("kind=%s ", str.c_str());
+        if (state && state->fDisplayable)
+            state->fDisplayable->SkDisplayable::dump(&maker);
+        else
+            SkDebugf("\n");
+    }
+#endif*/
+    bool handled = false;
+    SkDisplayable** firstMovie = maker.fMovies.begin();
+    SkDisplayable** endMovie = maker.fMovies.end();
+    for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) {
+        SkDisplayMovie* movie = (SkDisplayMovie*) *ptr;
+        if (kind != SkDisplayEvent::kOnload)
+            movie->doEvent(kind, state);
+    }
+    SkDisplayable* displayable = state ? state->fDisplayable : NULL;
+    int keyCode = state ? state->fCode : 0;
+    int count = fEvents.count();
+    for (int index = 0; index < count; index++) {
+        SkDisplayEvent* evt = fEvents[index];
+        if (evt->disable)
+            continue;
+        if (evt->kind != kind)
+            continue;
+        if (evt->code != (SkKey) -1) {
+            if ((int) evt->code > keyCode || (int) (evt->fMax != (SkKey) -1 ? evt->fMax : evt->code) < keyCode)
+                continue;
+            evt->fLastCode = (SkKey) keyCode;
+        }
+        if (evt->fTarget != NULL && evt->fTarget != displayable)
+            continue;
+        if (state == NULL || state->fDisable == 0) {
+            if (kind >= SkDisplayEvent::kMouseDown && kind <= SkDisplayEvent::kMouseUp) {
+                evt->x = state->fX;
+                evt->y = state->fY;
+            }
+            if (evt->enableEvent(maker))
+                fError = true;
+        }
+        handled = true;
+    }
+    return handled;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkEvents::dump(SkAnimateMaker& maker) {
+    int index;
+    SkTDDrawableArray& drawArray = maker.fDisplayList.fDrawList;
+    int count = drawArray.count();
+    for (index = 0; index < count; index++) {
+        SkDrawable* drawable = drawArray[index];
+        drawable->dumpEvents(); 
+    }
+    count = fEvents.count();
+    for (index = 0; index < count; index++) {
+        SkDisplayEvent* evt = fEvents[index];
+        evt->dumpEvent(&maker);
+    }
+}
+#endif
+
+// currently this only removes onLoad events
+void SkEvents::removeEvent(SkDisplayEvent::Kind kind, SkEventState* state) {
+    int keyCode = state ? state->fCode : 0;
+    SkDisplayable* displayable = state ? state->fDisplayable : NULL;
+    for (SkDisplayEvent** evtPtr = fEvents.begin(); evtPtr < fEvents.end(); evtPtr++) {
+        SkDisplayEvent* evt = *evtPtr;
+        if (evt->kind != kind)
+            continue;
+        if (evt->code != (SkKey) -1) {
+            if ((int) evt->code > keyCode || (int) (evt->fMax != (SkKey) -1 ? evt->fMax : evt->code) < keyCode)
+                continue;
+        }
+        if (evt->fTarget != NULL && evt->fTarget != displayable)
+            continue;
+        int index = fEvents.find(evt);
+        fEvents.remove(index);
+    }
+}
diff --git a/legacy/src/animator/SkDisplayEvents.h b/legacy/src/animator/SkDisplayEvents.h
new file mode 100644
index 0000000..2874ac5
--- /dev/null
+++ b/legacy/src/animator/SkDisplayEvents.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 SkDisplayEvents_DEFINED
+#define SkDisplayEvents_DEFINED
+
+#include "SkEvent.h"
+#include "SkDisplayEvent.h"
+
+struct SkEventState {
+    SkEventState();
+    int fCode;
+    SkBool fDisable;
+    SkDisplayable* fDisplayable;
+    SkScalar fX;
+    SkScalar fY;
+};
+
+class SkEvents {
+public:
+    SkEvents();
+    ~SkEvents();
+    void addEvent(SkDisplayEvent* evt) { *fEvents.append() = evt; }
+    bool doEvent(SkAnimateMaker& , SkDisplayEvent::Kind , SkEventState* );
+#ifdef SK_DUMP_ENABLED
+    void dump(SkAnimateMaker& );
+#endif
+    void reset() { fEvents.reset(); }
+    void removeEvent(SkDisplayEvent::Kind kind, SkEventState* );
+private:
+    SkTDDisplayEventArray fEvents;
+    SkBool fError;
+    friend class SkDisplayXMLParser;
+};
+
+#endif // SkDisplayEvents_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayInclude.cpp b/legacy/src/animator/SkDisplayInclude.cpp
new file mode 100644
index 0000000..860264e
--- /dev/null
+++ b/legacy/src/animator/SkDisplayInclude.cpp
@@ -0,0 +1,59 @@
+
+/*
+ * 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 "SkDisplayInclude.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimator.h"
+
+#if 0
+#undef SK_MEMBER
+#define SK_MEMBER(_member, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS::_A, _member), SkType_##_type, \
+    sizeof(((BASE_CLASS::_A*) 0)->_member) / sizeof(SkScalar) }
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkInclude::fInfo[] = {
+    SK_MEMBER(src, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkInclude);
+
+//SkInclude::SkInclude() {
+//  src.init();
+//}
+
+//SkInclude::~SkInclude() {
+//  src.unref();
+//}
+
+bool SkInclude::enable(SkAnimateMaker & ) {
+    return true;
+}
+
+bool SkInclude::hasEnable() const {
+    return true;
+}
+
+void SkInclude::onEndElement(SkAnimateMaker& maker) {
+    maker.fInInclude = true;
+    if (src.size() == 0 || maker.decodeURI(src.c_str()) == false) {
+        if (maker.getErrorCode() != SkXMLParserError::kNoError || maker.getNativeCode() != -1) {
+            maker.setInnerError(&maker, src);
+            maker.setErrorCode(SkDisplayXMLParserError::kInInclude);
+        } else {
+            maker.setErrorNoun(src);
+            maker.setErrorCode(SkDisplayXMLParserError::kIncludeNameUnknownOrMissing);
+        }
+    }
+    maker.fInInclude = false;
+}
diff --git a/legacy/src/animator/SkDisplayInclude.h b/legacy/src/animator/SkDisplayInclude.h
new file mode 100644
index 0000000..d3fe4da
--- /dev/null
+++ b/legacy/src/animator/SkDisplayInclude.h
@@ -0,0 +1,26 @@
+
+/*
+ * 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 SkDisplayInclude_DEFINED
+#define SkDisplayInclude_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkInclude : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Include);
+    virtual void onEndElement(SkAnimateMaker & );
+    virtual bool enable(SkAnimateMaker & );
+    virtual bool hasEnable() const;
+protected:
+    SkString src;
+};
+
+#endif // SkDisplayInclude_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayInput.cpp b/legacy/src/animator/SkDisplayInput.cpp
new file mode 100644
index 0000000..77bc5de
--- /dev/null
+++ b/legacy/src/animator/SkDisplayInput.cpp
@@ -0,0 +1,55 @@
+
+/*
+ * 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 "SkDisplayInput.h"
+
+enum SkInput_Properties {
+    SK_PROPERTY(initialized)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkInput::fInfo[] = {
+    SK_MEMBER_ALIAS(float, fFloat, Float),
+    SK_MEMBER_PROPERTY(initialized, Boolean),
+    SK_MEMBER_ALIAS(int, fInt, Int),
+    SK_MEMBER(name, String),
+    SK_MEMBER(string, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkInput);
+
+SkInput::SkInput() : fInt((int) SK_NaN32), fFloat(SK_ScalarNaN) {}
+
+SkDisplayable* SkInput::contains(const SkString& string) {
+    return string.equals(name) ? this : NULL;
+}
+
+bool SkInput::enable(SkAnimateMaker & ) {
+    return true;
+}
+
+bool SkInput::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(initialized):
+            value->fType = SkType_Boolean;
+            value->fOperand.fS32 = fInt != (int) SK_NaN32 ||
+                SkScalarIsNaN(fFloat) == false || string.size() > 0;
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+ 
+bool SkInput::hasEnable() const {
+    return true;
+}
diff --git a/legacy/src/animator/SkDisplayInput.h b/legacy/src/animator/SkDisplayInput.h
new file mode 100644
index 0000000..79d82b5
--- /dev/null
+++ b/legacy/src/animator/SkDisplayInput.h
@@ -0,0 +1,34 @@
+
+/*
+ * 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 SkDisplayInput_DEFINED
+#define SkDisplayInput_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkInput : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Input);
+    SkInput();
+    virtual SkDisplayable* contains(const SkString& );
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool enable(SkAnimateMaker & );
+    virtual bool hasEnable() const;
+protected:
+    SkString name;
+    int32_t  fInt;
+    SkScalar fFloat;
+    SkString string;
+private:
+    friend class SkDisplayEvent;
+    friend class SkPost;
+};
+
+#endif // SkDisplayInput_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayList.cpp b/legacy/src/animator/SkDisplayList.cpp
new file mode 100644
index 0000000..8424b12
--- /dev/null
+++ b/legacy/src/animator/SkDisplayList.cpp
@@ -0,0 +1,160 @@
+
+/*
+ * 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 "SkDisplayList.h"
+#include "SkAnimateActive.h"
+#include "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkDrawable.h"
+#include "SkDrawGroup.h"
+#include "SkDrawMatrix.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+
+SkDisplayList::SkDisplayList() : fDrawBounds(true), fUnionBounds(false), fInTime(0) {
+}
+
+SkDisplayList::~SkDisplayList() {
+}
+
+void SkDisplayList::append(SkActive* active) {
+    *fActiveList.append() = active;
+}
+
+bool SkDisplayList::draw(SkAnimateMaker& maker, SkMSec inTime) {
+    validate();
+    fInTime = inTime;
+    bool result = false;
+    fInvalBounds.setEmpty();
+    if (fDrawList.count()) {
+        for (SkActive** activePtr = fActiveList.begin(); activePtr < fActiveList.end(); activePtr++) {
+            SkActive* active = *activePtr;
+            active->reset();
+        }
+        for (int index = 0; index < fDrawList.count(); index++) {
+            SkDrawable* draw = fDrawList[index];
+            draw->initialize(); // allow matrices to reset themselves
+            SkASSERT(draw->isDrawable());
+            validate();
+            result |= draw->draw(maker);
+        }
+    }
+    validate();
+    return result;
+}
+
+int SkDisplayList::findGroup(SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
+    *parent = NULL;
+    *list = &fDrawList;
+    *grandList = &fDrawList;
+    return SearchForMatch(match, list, parent, found, grandList);
+}
+
+void SkDisplayList::hardReset() {
+    fDrawList.reset();
+    fActiveList.reset();
+}
+
+bool SkDisplayList::onIRect(const SkIRect& r) {
+    fBounds = r;
+    return fDrawBounds;
+}
+
+int SkDisplayList::SearchForMatch(SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
+    *found = NULL;
+    for (int index = 0; index < (*list)->count(); index++) {
+        SkDrawable* draw = (**list)[index];
+        if (draw == match)
+            return index;
+        if (draw->isApply()) {
+            SkApply* apply = (SkApply*) draw;
+            if (apply->scope == match)
+                return index;
+            if (apply->scope->isGroup() && SearchGroupForMatch(apply->scope, match, list, parent, found, grandList, index))
+                return index;
+            if (apply->mode == SkApply::kMode_create) {
+                for (SkDrawable** ptr = apply->fScopes.begin(); ptr < apply->fScopes.end(); ptr++) {
+                    SkDrawable* scope = *ptr;
+                    if (scope == match)
+                        return index;
+                    //perhaps should call SearchGroupForMatch here as well (on scope)
+                }
+            } 
+        }
+        if (draw->isGroup() && SearchGroupForMatch(draw, match, list, parent, found, grandList, index)) 
+            return index;
+        
+    }
+    return -1;
+}
+
+bool SkDisplayList::SearchGroupForMatch(SkDrawable* draw, SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList, int &index) {
+            SkGroup* group = (SkGroup*) draw;
+            if (group->getOriginal() == match)
+                return true;
+            SkTDDrawableArray* saveList = *list;
+            int groupIndex = group->findGroup(match, list, parent, found, grandList);
+            if (groupIndex >= 0) {
+                *found = group;
+                index = groupIndex;
+                return true;
+            }
+            *list = saveList;
+            return false;
+        }
+
+void SkDisplayList::reset() {
+    for (int index = 0; index < fDrawList.count(); index++) {
+        SkDrawable* draw = fDrawList[index];
+        if (draw->isApply() == false)
+            continue;
+        SkApply* apply = (SkApply*) draw;
+        apply->reset();
+    }           
+}
+
+void SkDisplayList::remove(SkActive* active) {
+    int index = fActiveList.find(active);
+    SkASSERT(index >= 0);
+    fActiveList.remove(index);  // !!! could use shuffle instead
+    SkASSERT(fActiveList.find(active) < 0);
+}
+
+#ifdef SK_DUMP_ENABLED
+int SkDisplayList::fDumpIndex;
+int SkDisplayList::fIndent;
+
+void SkDisplayList::dump(SkAnimateMaker* maker) {
+    fIndent = 0;
+    dumpInner(maker);
+}
+
+void SkDisplayList::dumpInner(SkAnimateMaker* maker) {
+    for (int index = 0; index < fDrawList.count(); index++) {
+        fDumpIndex = index;
+        fDrawList[fDumpIndex]->dump(maker);
+    }
+}
+
+#endif
+
+#ifdef SK_DEBUG
+void SkDisplayList::validate() {
+    for (int index = 0; index < fDrawList.count(); index++) {
+        SkDrawable* draw = fDrawList[index];
+        draw->validate();
+    }
+}
+#endif
+
+
diff --git a/legacy/src/animator/SkDisplayList.h b/legacy/src/animator/SkDisplayList.h
new file mode 100644
index 0000000..f520b55
--- /dev/null
+++ b/legacy/src/animator/SkDisplayList.h
@@ -0,0 +1,71 @@
+
+/*
+ * 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 SkDisplayList_DEFINED
+#define SkDisplayList_DEFINED
+
+#include "SkOperand.h"
+#include "SkIntArray.h"
+#include "SkBounder.h"
+#include "SkRect.h"
+
+class SkAnimateMaker;
+class SkActive;
+class SkApply;
+class SkDrawable;
+class SkGroup;
+
+class SkDisplayList : public SkBounder {
+public:
+    SkDisplayList();
+    virtual ~SkDisplayList();
+    void append(SkActive* );
+    void clear() { fDrawList.reset(); }
+    int count() { return fDrawList.count(); }
+    bool draw(SkAnimateMaker& , SkMSec time);
+#ifdef SK_DUMP_ENABLED
+    void dump(SkAnimateMaker* maker);
+    void dumpInner(SkAnimateMaker* maker);
+    static int fIndent;
+    static int fDumpIndex;
+#endif
+    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; }
+    void hardReset();
+    virtual bool onIRect(const SkIRect& r);
+    void reset();
+    void remove(SkActive* );
+#ifdef SK_DEBUG
+    void validate();
+#else
+    void validate() {}
+#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, 
+        int &index);
+public:
+    SkIRect fBounds;
+    SkIRect fInvalBounds;
+    bool fDrawBounds;
+    bool fHasUnion;
+    bool fUnionBounds;
+private:
+    SkTDDrawableArray fDrawList;
+    SkTDActiveArray fActiveList;
+    SkMSec fInTime;
+    friend class SkEvents;
+};
+
+#endif // SkDisplayList_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayMath.cpp b/legacy/src/animator/SkDisplayMath.cpp
new file mode 100644
index 0000000..c4d496d
--- /dev/null
+++ b/legacy/src/animator/SkDisplayMath.cpp
@@ -0,0 +1,240 @@
+
+/*
+ * 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 "SkDisplayMath.h"
+
+enum SkDisplayMath_Properties {
+    SK_PROPERTY(E),
+    SK_PROPERTY(LN10),
+    SK_PROPERTY(LN2),
+    SK_PROPERTY(LOG10E),
+    SK_PROPERTY(LOG2E),
+    SK_PROPERTY(PI),
+    SK_PROPERTY(SQRT1_2),
+    SK_PROPERTY(SQRT2)
+};
+
+const SkScalar SkDisplayMath::gConstants[] = {
+#ifdef SK_SCALAR_IS_FLOAT
+    2.718281828f,   // E
+    2.302585093f,   // LN10
+    0.693147181f,   // LN2
+    0.434294482f,   // LOG10E
+    1.442695041f,   // LOG2E
+    3.141592654f,   // PI
+    0.707106781f,   // SQRT1_2
+    1.414213562f        // SQRT2 
+#else
+    0x2B7E1,    // E
+    0x24D76,    // LN10
+    0xB172,     // LN2
+    0x6F2E,     // LOG10E
+    0x17154,    // LOG2E
+    0x3243F,    // PI
+    0xB505,     // SQRT1_2
+    0x16A0A // SQRT2
+#endif
+};
+
+enum SkDisplayMath_Functions {
+    SK_FUNCTION(abs),
+    SK_FUNCTION(acos),
+    SK_FUNCTION(asin),
+    SK_FUNCTION(atan),
+    SK_FUNCTION(atan2),
+    SK_FUNCTION(ceil),
+    SK_FUNCTION(cos),
+    SK_FUNCTION(exp),
+    SK_FUNCTION(floor),
+    SK_FUNCTION(log),
+    SK_FUNCTION(max),
+    SK_FUNCTION(min),
+    SK_FUNCTION(pow),
+    SK_FUNCTION(random),
+    SK_FUNCTION(round),
+    SK_FUNCTION(sin),
+    SK_FUNCTION(sqrt),
+    SK_FUNCTION(tan)
+};
+
+const SkFunctionParamType SkDisplayMath::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_Float, // abs
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // acos
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // asin
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // atan
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // atan2
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // ceil
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // cos
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // exp
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // floor
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // log
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Array, // max
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Array, // min
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // pow
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // random
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // round
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // sin
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // sqrt
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // tan
+    (SkFunctionParamType) 0
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayMath::fInfo[] = {
+    SK_MEMBER_PROPERTY(E, Float),
+    SK_MEMBER_PROPERTY(LN10, Float),
+    SK_MEMBER_PROPERTY(LN2, Float),
+    SK_MEMBER_PROPERTY(LOG10E, Float),
+    SK_MEMBER_PROPERTY(LOG2E, Float),
+    SK_MEMBER_PROPERTY(PI, Float),
+    SK_MEMBER_PROPERTY(SQRT1_2, Float),
+    SK_MEMBER_PROPERTY(SQRT2, Float),
+    SK_MEMBER_FUNCTION(abs, Float),
+    SK_MEMBER_FUNCTION(acos, Float),
+    SK_MEMBER_FUNCTION(asin, Float),
+    SK_MEMBER_FUNCTION(atan, Float),
+    SK_MEMBER_FUNCTION(atan2, Float),
+    SK_MEMBER_FUNCTION(ceil, Float),
+    SK_MEMBER_FUNCTION(cos, Float),
+    SK_MEMBER_FUNCTION(exp, Float),
+    SK_MEMBER_FUNCTION(floor, Float),
+    SK_MEMBER_FUNCTION(log, Float),
+    SK_MEMBER_FUNCTION(max, Float),
+    SK_MEMBER_FUNCTION(min, Float),
+    SK_MEMBER_FUNCTION(pow, Float),
+    SK_MEMBER_FUNCTION(random, Float),
+    SK_MEMBER_FUNCTION(round, Float),
+    SK_MEMBER_FUNCTION(sin, Float),
+    SK_MEMBER_FUNCTION(sqrt, Float),
+    SK_MEMBER_FUNCTION(tan, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayMath);
+
+void SkDisplayMath::executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* scriptValue) {
+    if (scriptValue == NULL)
+        return;
+    SkASSERT(target == this);
+    SkScriptValue* array = parameters.begin();
+    SkScriptValue* end = parameters.end();
+    SkScalar input = parameters[0].fOperand.fScalar;
+    SkScalar scalarResult;
+    switch (index) {
+        case SK_FUNCTION(abs):
+            scalarResult = SkScalarAbs(input); 
+            break;
+        case SK_FUNCTION(acos):
+            scalarResult = SkScalarACos(input);
+            break;
+        case SK_FUNCTION(asin):
+            scalarResult = SkScalarASin(input);
+            break;
+        case SK_FUNCTION(atan):
+            scalarResult = SkScalarATan2(input, SK_Scalar1);
+            break;
+        case SK_FUNCTION(atan2):
+            scalarResult = SkScalarATan2(input, parameters[1].fOperand.fScalar);
+            break;
+        case SK_FUNCTION(ceil):
+            scalarResult = SkIntToScalar(SkScalarCeil(input)); 
+            break;
+        case SK_FUNCTION(cos):
+            scalarResult = SkScalarCos(input);
+            break;
+        case SK_FUNCTION(exp):
+            scalarResult = SkScalarExp(input);
+            break;
+        case SK_FUNCTION(floor):
+            scalarResult = SkIntToScalar(SkScalarFloor(input)); 
+            break;
+        case SK_FUNCTION(log):
+            scalarResult = SkScalarLog(input);
+            break;
+        case SK_FUNCTION(max):
+            scalarResult = -SK_ScalarMax;
+            while (array < end) {
+                scalarResult = SkMaxScalar(scalarResult, array->fOperand.fScalar);
+                array++;
+            }
+            break;
+        case SK_FUNCTION(min):
+            scalarResult = SK_ScalarMax;
+            while (array < end) {
+                scalarResult = SkMinScalar(scalarResult, array->fOperand.fScalar);
+                array++;
+            }
+            break;
+        case SK_FUNCTION(pow):
+            // not the greatest -- but use x^y = e^(y * ln(x))
+            scalarResult = SkScalarLog(input);
+            scalarResult = SkScalarMul(parameters[1].fOperand.fScalar, scalarResult);
+            scalarResult = SkScalarExp(scalarResult);
+            break;
+        case SK_FUNCTION(random):
+            scalarResult = fRandom.nextUScalar1();
+            break;
+        case SK_FUNCTION(round):
+            scalarResult = SkIntToScalar(SkScalarRound(input)); 
+            break;
+        case SK_FUNCTION(sin):
+            scalarResult = SkScalarSin(input);
+            break;
+        case SK_FUNCTION(sqrt): {
+            SkASSERT(parameters.count() == 1);
+            SkASSERT(type == SkType_Float);
+            scalarResult = SkScalarSqrt(input); 
+            } break;
+        case SK_FUNCTION(tan):
+            scalarResult = SkScalarTan(input);
+            break;
+        default:
+            SkASSERT(0);
+            scalarResult = SK_ScalarNaN;
+    }
+    scriptValue->fOperand.fScalar = scalarResult;
+    scriptValue->fType = SkType_Float;
+}
+
+const SkFunctionParamType* SkDisplayMath::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+bool SkDisplayMath::getProperty(int index, SkScriptValue* value) const {
+    if ((unsigned)index < SK_ARRAY_COUNT(gConstants)) {
+        value->fOperand.fScalar = gConstants[index];
+        value->fType = SkType_Float;
+        return true;
+    }
+    SkASSERT(0);
+    return false;
+}
diff --git a/legacy/src/animator/SkDisplayMath.h b/legacy/src/animator/SkDisplayMath.h
new file mode 100644
index 0000000..e21d03f
--- /dev/null
+++ b/legacy/src/animator/SkDisplayMath.h
@@ -0,0 +1,32 @@
+
+/*
+ * 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 SkDisplayMath_DEFINED
+#define SkDisplayMath_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkRandom.h"
+
+class SkDisplayMath : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Math);
+    virtual void executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+private:
+    mutable SkRandom fRandom;
+    static const SkScalar gConstants[];
+    static const SkFunctionParamType fFunctionParameters[];
+
+};
+    
+#endif // SkDisplayMath_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayMovie.cpp b/legacy/src/animator/SkDisplayMovie.cpp
new file mode 100644
index 0000000..4fbf3f3
--- /dev/null
+++ b/legacy/src/animator/SkDisplayMovie.cpp
@@ -0,0 +1,130 @@
+
+/*
+ * 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 "SkDisplayMovie.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayMovie::fInfo[] = {
+    SK_MEMBER(src, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayMovie);
+
+SkDisplayMovie::SkDisplayMovie() : fDecodedSuccessfully(false), fLoaded(false), fMovieBuilt(false) {
+    fMovie.fMaker->fInMovie = true;
+}
+
+SkDisplayMovie::~SkDisplayMovie() {
+}
+
+void SkDisplayMovie::buildMovie() {
+    if (fMovieBuilt)
+        return;
+    SkAnimateMaker* movieMaker = fMovie.fMaker;
+    SkAnimateMaker* parentMaker = movieMaker->fParentMaker;
+    if (src.size() == 0 || parentMaker == NULL)
+        return;
+    movieMaker->fPrefix.set(parentMaker->fPrefix);
+    fDecodedSuccessfully = fMovie.fMaker->decodeURI(src.c_str());
+    if (fDecodedSuccessfully == false) {
+
+        if (movieMaker->getErrorCode() != SkXMLParserError::kNoError || movieMaker->getNativeCode() != -1) {
+            movieMaker->setInnerError(parentMaker, src);
+            parentMaker->setErrorCode(SkDisplayXMLParserError::kInMovie);
+        } else {
+            parentMaker->setErrorNoun(src);
+            parentMaker->setErrorCode(SkDisplayXMLParserError::kMovieNameUnknownOrMissing);
+        }
+    }
+    fMovieBuilt = true;
+}
+
+SkDisplayable* SkDisplayMovie::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayMovie* copy = (SkDisplayMovie*) INHERITED::deepCopy(maker);
+    copy->fMovie.fMaker->fParentMaker = fMovie.fMaker->fParentMaker;
+    copy->fMovie.fMaker->fHostEventSinkID = fMovie.fMaker->fHostEventSinkID;
+    copy->fMovieBuilt = false;
+    *fMovie.fMaker->fParentMaker->fMovies.append() = copy;
+    return copy;
+}
+
+void SkDisplayMovie::dirty() {
+    buildMovie();
+}
+
+bool SkDisplayMovie::doEvent(SkDisplayEvent::Kind kind, SkEventState* state) {
+    if (fLoaded == false)
+        return false;
+    fMovie.fMaker->fEnableTime = fMovie.fMaker->fParentMaker->fEnableTime;
+    return fMovie.fMaker->fEvents.doEvent(*fMovie.fMaker, kind, state);
+}
+
+bool SkDisplayMovie::draw(SkAnimateMaker& maker) {
+    if (fDecodedSuccessfully == false)
+        return false;
+    if (fLoaded == false)
+        enable(maker);
+    maker.fCanvas->save();
+    SkPaint local = SkPaint(*maker.fPaint);
+    bool result = fMovie.draw(maker.fCanvas, &local, 
+        maker.fDisplayList.getTime()) != SkAnimator::kNotDifferent;
+    maker.fDisplayList.fInvalBounds.join(fMovie.fMaker->fDisplayList.fInvalBounds);
+    maker.fCanvas->restore();
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayMovie::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("src=\"%s\"/>\n",  src.c_str());
+    SkAnimateMaker* movieMaker = fMovie.fMaker;
+    SkDisplayList::fIndent += 4;
+    movieMaker->fDisplayList.dumpInner(movieMaker);
+    SkDisplayList::fIndent -= 4;
+    dumpEnd(maker);
+}
+
+void SkDisplayMovie::dumpEvents() {
+    fMovie.fMaker->fEvents.dump(*fMovie.fMaker);
+}
+#endif
+
+bool SkDisplayMovie::enable(SkAnimateMaker& maker) {
+    if (fDecodedSuccessfully == false)
+        return false;
+    SkAnimateMaker* movieMaker = fMovie.fMaker;
+    movieMaker->fEvents.doEvent(*movieMaker, SkDisplayEvent::kOnload, NULL);
+    movieMaker->fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
+    fLoaded = true;
+    movieMaker->fLoaded = true;
+    return false;
+}
+
+bool SkDisplayMovie::hasEnable() const {
+    return true;
+}
+
+void SkDisplayMovie::onEndElement(SkAnimateMaker& maker) {
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    fMovie.fMaker->fDebugTimeBase = maker.fDebugTimeBase;
+#endif
+    fMovie.fMaker->fPrefix.set(maker.fPrefix);
+    fMovie.fMaker->fHostEventSinkID = maker.fHostEventSinkID;
+    fMovie.fMaker->fParentMaker = &maker;
+    buildMovie();
+    *maker.fMovies.append() = this;
+}
+
+
diff --git a/legacy/src/animator/SkDisplayMovie.h b/legacy/src/animator/SkDisplayMovie.h
new file mode 100644
index 0000000..a599fb6
--- /dev/null
+++ b/legacy/src/animator/SkDisplayMovie.h
@@ -0,0 +1,52 @@
+
+/*
+ * 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 SkDisplayMovie_DEFINED
+#define SkDisplayMovie_DEFINED
+
+#include "SkAnimator.h"
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+
+struct SkEventState;
+
+class SkDisplayMovie : public SkDrawable {
+    DECLARE_DISPLAY_MEMBER_INFO(Movie);
+    SkDisplayMovie();
+    virtual ~SkDisplayMovie();
+    void buildMovie();
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual void dirty();
+    bool doEvent(const SkEvent& evt) {
+        return fLoaded && fMovie.doEvent(evt);
+    }
+    virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state );
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+    virtual void dumpEvents();
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    const SkAnimator* getAnimator() const { return &fMovie; }
+    virtual bool hasEnable() const;
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkString src;
+    SkAnimator fMovie;
+    SkBool8 fDecodedSuccessfully;
+    SkBool8 fLoaded;
+    SkBool8 fMovieBuilt;
+    friend class SkAnimateMaker;
+    friend class SkPost;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+#endif // SkDisplayMovie_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayNumber.cpp b/legacy/src/animator/SkDisplayNumber.cpp
new file mode 100644
index 0000000..282dab6
--- /dev/null
+++ b/legacy/src/animator/SkDisplayNumber.cpp
@@ -0,0 +1,59 @@
+
+/*
+ * 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 "SkDisplayNumber.h"
+
+enum SkDisplayNumber_Properties {
+    SK_PROPERTY(MAX_VALUE),
+    SK_PROPERTY(MIN_VALUE),
+    SK_PROPERTY(NEGATIVE_INFINITY),
+    SK_PROPERTY(NaN),
+    SK_PROPERTY(POSITIVE_INFINITY)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayNumber::fInfo[] = {
+    SK_MEMBER_PROPERTY(MAX_VALUE, Float),
+    SK_MEMBER_PROPERTY(MIN_VALUE, Float),
+    SK_MEMBER_PROPERTY(NEGATIVE_INFINITY, Float),
+    SK_MEMBER_PROPERTY(NaN, Float),
+    SK_MEMBER_PROPERTY(POSITIVE_INFINITY, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayNumber);
+
+bool SkDisplayNumber::getProperty(int index, SkScriptValue* value) const {
+    SkScalar constant;
+    switch (index) {
+        case SK_PROPERTY(MAX_VALUE):
+            constant = SK_ScalarMax;
+        break;
+        case SK_PROPERTY(MIN_VALUE):
+            constant = SK_ScalarMin;
+        break;
+        case SK_PROPERTY(NEGATIVE_INFINITY):
+            constant = -SK_ScalarInfinity;
+        break;
+        case SK_PROPERTY(NaN):
+            constant = SK_ScalarNaN;
+        break;
+        case SK_PROPERTY(POSITIVE_INFINITY):
+            constant = SK_ScalarInfinity;
+        break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fOperand.fScalar = constant;
+    value->fType = SkType_Float;
+    return true;
+}
diff --git a/legacy/src/animator/SkDisplayNumber.h b/legacy/src/animator/SkDisplayNumber.h
new file mode 100644
index 0000000..7092e21
--- /dev/null
+++ b/legacy/src/animator/SkDisplayNumber.h
@@ -0,0 +1,22 @@
+
+/*
+ * 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 SkDisplayNumber_DEFINED
+#define SkDisplayNumber_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkDisplayNumber : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Number);
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+private:
+};
+    
+#endif // SkDisplayNumber_DEFINED
diff --git a/legacy/src/animator/SkDisplayPost.cpp b/legacy/src/animator/SkDisplayPost.cpp
new file mode 100644
index 0000000..8283a9c
--- /dev/null
+++ b/legacy/src/animator/SkDisplayPost.cpp
@@ -0,0 +1,307 @@
+
+/*
+ * 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 "SkDisplayPost.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimator.h"
+#include "SkDisplayMovie.h"
+#include "SkPostParts.h"
+#include "SkScript.h"
+#ifdef SK_DEBUG
+#include "SkDump.h"
+#include "SkTime.h"
+#endif
+
+enum SkPost_Properties {
+    SK_PROPERTY(target),
+    SK_PROPERTY(type)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPost::fInfo[] = {
+    SK_MEMBER(delay, MSec),
+//  SK_MEMBER(initialized, Boolean),
+    SK_MEMBER(mode, EventMode),
+    SK_MEMBER(sink, String),
+    SK_MEMBER_PROPERTY(target, String),
+    SK_MEMBER_PROPERTY(type, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPost);
+
+SkPost::SkPost() : delay(0), /*initialized(SkBool(-1)), */ mode(kImmediate), fMaker(NULL),
+    fSinkID(0), fTargetMaker(NULL), fChildHasID(false), fDirty(false) {
+}
+
+SkPost::~SkPost() {
+    for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++)
+        delete *part;
+}
+
+bool SkPost::add(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child && child->isDataInput());
+    SkDataInput* part = (SkDataInput*) child;
+    *fParts.append() = part;
+    return true;
+}
+
+bool SkPost::childrenNeedDisposing() const { 
+    return false; 
+}
+
+void SkPost::dirty() { 
+    fDirty = true; 
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkPost::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkString* eventType = new SkString();
+    fEvent.getType(eventType);
+    if (eventType->equals("user")) {
+        const char* target = fEvent.findString("id");
+        SkDebugf("target=\"%s\" ", target);
+    }
+    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) ");
+    SkString string;
+    SkDump::GetEnumString(SkType_EventMode, mode, &string);
+    if (!string.equals("immediate"))
+        SkDebugf("mode=\"%s\" ", string.c_str());
+    // !!! could enhance this to search through make hierarchy to show name of sink
+    if (sink.size() > 0) {
+        SkDebugf("sink=\"%s\" sinkID=\"%d\" ", sink.c_str(), fSinkID);
+    } else if (fSinkID != maker->getAnimator()->getSinkID() && fSinkID != 0) {
+        SkDebugf("sinkID=\"%d\" ", fSinkID);
+    }
+    const SkMetaData& meta = fEvent.getMetaData();
+    SkMetaData::Iter iter(meta);
+    SkMetaData::Type    type;
+    int number;
+    const char* name;
+    bool closedYet = false;
+    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 
+    //data itself
+    //SkDataInput** ptr = fParts.end();
+    //SkDataInput* data;
+    //const char* ID;
+    while ((name = iter.next(&type, &number)) != NULL) {
+        //ptr--;
+        if (strcmp(name, "id") == 0)
+            continue;
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        //data = *ptr;
+        //if (data->id)
+        //    ID = data->id;
+        //else
+        //    ID = "";
+        SkDebugf("%*s<data name=\"%s\" ", SkDisplayList::fIndent, "", name);
+        switch (type) {
+            case SkMetaData::kS32_Type: {
+                int32_t s32;
+                meta.findS32(name, &s32);
+                SkDebugf("int=\"%d\" ", s32);
+                } break;
+            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));
+                break;
+            case SkMetaData::kPtr_Type: {//when do we have a pointer
+                    void* ptr;
+                    meta.findPtr(name, &ptr);
+                    SkDebugf("0x%08x ", ptr);
+                } break;
+            case SkMetaData::kBool_Type: {
+                bool boolean;
+                meta.findBool(name, &boolean);
+                SkDebugf("boolean=\"%s\" ", boolean ? "true " : "false ");
+                } break;
+            default:
+                break;
+        }
+        SkDebugf("/>\n");
+        //ptr++;
+/*      perhaps this should only be done in the case of a pointer?
+        SkDisplayable* displayable;
+        if (maker->find(name, &displayable))
+            displayable->dump(maker);
+        else
+            SkDebugf("\n");*/
+    }
+    SkDisplayList::fIndent -= 4;
+    if (closedYet)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+
+}
+#endif
+
+bool SkPost::enable(SkAnimateMaker& maker ) {
+    if (maker.hasError())
+        return true;
+    if (fDirty) {
+        if (sink.size() > 0)
+            findSinkID();
+        if (fChildHasID) {
+            SkString preserveID(fEvent.findString("id"));
+            fEvent.getMetaData().reset();
+            if (preserveID.size() > 0)
+                fEvent.setString("id", preserveID);
+            for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++) {
+                if ((*part)->add())
+                    maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingDataToPost);
+            }
+        }
+        fDirty = false;
+    }
+#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpPosts) {
+        SkDebugf("post enable: ");
+        dump(&maker);
+    }
+#if defined SK_DEBUG_ANIMATION_TIMING
+    SkString debugOut;
+    SkMSec time = maker.getAppTime();
+    debugOut.appendS32(time - maker.fDebugTimeBase);
+    debugOut.append(" post id=");
+    debugOut.append(_id);
+    debugOut.append(" enable=");
+    debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
+    debugOut.append(" delay=");
+    debugOut.appendS32(delay);
+#endif
+#endif
+//  SkMSec adjustedDelay = maker.adjustDelay(maker.fEnableTime, delay);
+    SkMSec futureTime = maker.fEnableTime + delay;
+    fEvent.setFast32(futureTime);
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    debugOut.append(" future=");
+    debugOut.appendS32(futureTime - maker.fDebugTimeBase);
+    SkDebugf("%s\n", debugOut.c_str());
+#endif
+    SkEventSinkID targetID = fSinkID;
+    bool isAnimatorEvent = true;
+    SkAnimator* anim = maker.getAnimator();
+    if (targetID == 0) {
+        isAnimatorEvent = fEvent.findString("id") != NULL;
+        if (isAnimatorEvent) 
+            targetID = anim->getSinkID();
+        else if (maker.fHostEventSinkID)
+            targetID = maker.fHostEventSinkID;
+        else
+            return true;
+    } else
+        anim = fTargetMaker->getAnimator();
+    if (delay == 0) {
+        if (isAnimatorEvent && mode == kImmediate)
+            fTargetMaker->doEvent(fEvent);
+        else
+            anim->onEventPost(new SkEvent(fEvent), targetID);
+    } else
+        anim->onEventPostTime(new SkEvent(fEvent), targetID, futureTime);
+    return true;
+}
+
+void SkPost::findSinkID() {
+    // get the next delimiter '.' if any
+    fTargetMaker = fMaker;
+    const char* ch = sink.c_str();
+    do {
+        const char* end = strchr(ch, '.');
+        size_t len = end ? end - ch : strlen(ch);
+        SkDisplayable* displayable = NULL;
+        if (SK_LITERAL_STR_EQUAL("parent", ch, len)) {
+            if (fTargetMaker->fParentMaker)
+                fTargetMaker = fTargetMaker->fParentMaker;
+            else {
+                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kNoParentAvailable);
+                return;
+            }
+        } else {
+            fTargetMaker->find(ch, len, &displayable);
+            if (displayable == NULL || displayable->getType() != SkType_Movie) {
+                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie);
+                return;
+            }
+            SkDisplayMovie* movie = (SkDisplayMovie*) displayable;
+            fTargetMaker = movie->fMovie.fMaker;
+        }
+        if (end == NULL)
+            break;
+        ch = ++end;
+    } while (true);
+    SkAnimator* anim = fTargetMaker->getAnimator();
+    fSinkID = anim->getSinkID();
+}
+ 
+bool SkPost::hasEnable() const {
+    return true;
+}
+
+void SkPost::onEndElement(SkAnimateMaker& maker) {
+    fTargetMaker = fMaker = &maker;
+    if (fChildHasID == false) {
+        for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++)
+            delete *part;
+        fParts.reset();
+    }
+}
+
+void SkPost::setChildHasID() { 
+    fChildHasID = true; 
+}
+
+bool SkPost::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(value.fType == SkType_String);
+    SkString* string = value.fOperand.fString;
+    switch(index) {
+        case SK_PROPERTY(target): {
+            fEvent.setType("user");
+            fEvent.setString("id", *string);
+            mode = kImmediate;
+            } break;
+        case SK_PROPERTY(type):
+            fEvent.setType(*string);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
diff --git a/legacy/src/animator/SkDisplayPost.h b/legacy/src/animator/SkDisplayPost.h
new file mode 100644
index 0000000..b417bab
--- /dev/null
+++ b/legacy/src/animator/SkDisplayPost.h
@@ -0,0 +1,59 @@
+
+/*
+ * 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 SkDisplayPost_DEFINED
+#define SkDisplayPost_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkEvent.h"
+#include "SkEventSink.h"
+#include "SkMemberInfo.h"
+#include "SkIntArray.h"
+
+class SkDataInput;
+class SkAnimateMaker;
+
+class SkPost : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Post);
+    enum Mode {
+        kDeferred,
+        kImmediate
+    };
+    SkPost();
+    virtual ~SkPost();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool childrenNeedDisposing() const;
+    virtual void dirty();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void setChildHasID();
+    virtual bool setProperty(int index, SkScriptValue& );
+protected:
+    SkMSec delay;
+    SkString sink;
+//  SkBool initialized;
+    Mode mode;
+    SkEvent fEvent;
+    SkAnimateMaker* fMaker;
+    SkTDDataArray fParts;
+    SkEventSinkID fSinkID;
+    SkAnimateMaker* fTargetMaker;
+    SkBool8 fChildHasID;
+    SkBool8 fDirty;
+private:
+    void findSinkID();
+    friend class SkDataInput;
+    typedef SkDisplayable INHERITED;
+};
+
+#endif //SkDisplayPost_DEFINED
diff --git a/legacy/src/animator/SkDisplayRandom.cpp b/legacy/src/animator/SkDisplayRandom.cpp
new file mode 100644
index 0000000..ac6c3a1
--- /dev/null
+++ b/legacy/src/animator/SkDisplayRandom.cpp
@@ -0,0 +1,72 @@
+
+/*
+ * 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 "SkDisplayRandom.h"
+#include "SkInterpolator.h"
+
+enum SkDisplayRandom_Properties {
+    SK_PROPERTY(random),
+    SK_PROPERTY(seed)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayRandom::fInfo[] = {
+    SK_MEMBER(blend, Float),
+    SK_MEMBER(max, Float),
+    SK_MEMBER(min, Float),
+    SK_MEMBER_DYNAMIC_PROPERTY(random, Float),
+    SK_MEMBER_PROPERTY(seed, Int)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayRandom);
+
+SkDisplayRandom::SkDisplayRandom() : blend(0), min(0), max(SK_Scalar1) {
+}
+
+#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("/>\n");
+}
+#endif
+
+bool SkDisplayRandom::getProperty(int index, SkScriptValue* value) const {
+    switch(index) {
+        case SK_PROPERTY(random): {
+            SkScalar random = fRandom.nextUScalar1();
+            SkScalar relativeT = SkUnitCubicInterp(random, SK_Scalar1 - blend, 0, 0, SK_Scalar1 - blend);
+            value->fOperand.fScalar = min + SkScalarMul(max - min, relativeT);
+            value->fType = SkType_Float;
+            return true;
+        }
+        default:
+            SkASSERT(0);
+    }
+    return false;
+}
+
+bool SkDisplayRandom::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(index == SK_PROPERTY(seed));
+    SkASSERT(value.fType == SkType_Int);
+    fRandom.setSeed(value.fOperand.fS32);
+    return true;
+}
+
diff --git a/legacy/src/animator/SkDisplayRandom.h b/legacy/src/animator/SkDisplayRandom.h
new file mode 100644
index 0000000..87956d2
--- /dev/null
+++ b/legacy/src/animator/SkDisplayRandom.h
@@ -0,0 +1,41 @@
+
+/*
+ * 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 SkDisplayRandom_DEFINED
+#define SkDisplayRandom_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkRandom.h"
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+class SkDisplayRandom : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Random);
+    SkDisplayRandom();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setProperty(int index, SkScriptValue& );
+private:
+    SkScalar blend;
+    SkScalar min;
+    SkScalar max;
+    mutable SkRandom fRandom;
+};
+
+#endif // SkDisplayRandom_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayScreenplay.cpp b/legacy/src/animator/SkDisplayScreenplay.cpp
new file mode 100644
index 0000000..463f948
--- /dev/null
+++ b/legacy/src/animator/SkDisplayScreenplay.cpp
@@ -0,0 +1,22 @@
+
+/*
+ * 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 "SkDisplayScreenplay.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayScreenplay::fInfo[] = {
+    SK_MEMBER(time, MSec)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayScreenplay);
+
+
diff --git a/legacy/src/animator/SkDisplayScreenplay.h b/legacy/src/animator/SkDisplayScreenplay.h
new file mode 100644
index 0000000..0265548
--- /dev/null
+++ b/legacy/src/animator/SkDisplayScreenplay.h
@@ -0,0 +1,21 @@
+
+/*
+ * 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 SkDisplayScreenplay_DEFINED
+#define SkDisplayScreenplay_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkDisplayScreenplay : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Screenplay);
+    SkMSec time;
+};
+
+#endif // SkDisplayScreenplay_DEFINED
diff --git a/legacy/src/animator/SkDisplayType.cpp b/legacy/src/animator/SkDisplayType.cpp
new file mode 100644
index 0000000..4dfa62f
--- /dev/null
+++ b/legacy/src/animator/SkDisplayType.cpp
@@ -0,0 +1,766 @@
+
+/*
+ * 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 "SkDisplayType.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+#include "SkDisplayAdd.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayBounds.h"
+#include "SkDisplayEvent.h"
+#include "SkDisplayInclude.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+#include "SkDisplayMath.h"
+#include "SkDisplayMovie.h"
+#include "SkDisplayNumber.h"
+#include "SkDisplayPost.h"
+#include "SkDisplayRandom.h"
+#include "SkDisplayTypes.h"
+#include "SkDraw3D.h"
+#include "SkDrawBitmap.h"
+#include "SkDrawClip.h"
+#include "SkDrawDash.h"
+#include "SkDrawDiscrete.h"
+#include "SkDrawEmboss.h"
+#include "SkDrawFull.h"
+#include "SkDrawGradient.h"
+#include "SkDrawLine.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawOval.h"
+#include "SkDrawPaint.h"
+#include "SkDrawPath.h"
+#include "SkDrawPoint.h"
+#include "SkDrawSaveLayer.h"
+#include "SkDrawText.h"
+#include "SkDrawTextBox.h"
+#include "SkDrawTo.h"
+#include "SkDrawTransparentShader.h"
+#include "SkDump.h"
+#include "SkExtras.h"
+#include "SkHitClear.h"
+#include "SkHitTest.h"
+#include "SkMatrixParts.h"
+#include "SkPathParts.h"
+#include "SkPostParts.h"
+#include "SkSnapshot.h"
+#include "SkTextOnPath.h"
+#include "SkTextToPath.h"
+#include "SkTSearch.h"
+
+#define CASE_NEW(_class) \
+    case SkType_##_class: result = new Sk##_class(); break
+#define CASE_DRAW_NEW(_class) \
+    case SkType_##_class: result = new SkDraw##_class(); break
+#define CASE_DISPLAY_NEW(_class) \
+    case SkType_##_class: result = new SkDisplay##_class(); break
+#ifdef SK_DEBUG
+    #define CASE_DEBUG_RETURN_NIL(_class) \
+        case SkType_##_class: return NULL
+#else
+    #define CASE_DEBUG_RETURN_NIL(_class)
+#endif
+    
+
+SkDisplayTypes SkDisplayType::gNewTypes = kNumberOfTypes;
+
+SkDisplayable* SkDisplayType::CreateInstance(SkAnimateMaker* maker, SkDisplayTypes type) {
+    SkDisplayable* result = NULL;
+    switch (type) {
+        // unknown
+        CASE_DISPLAY_NEW(Math);
+        CASE_DISPLAY_NEW(Number);
+        CASE_NEW(Add);
+        CASE_NEW(AddCircle);
+        // addgeom
+        CASE_DEBUG_RETURN_NIL(AddMode);
+        CASE_NEW(AddOval);
+        CASE_NEW(AddPath);
+        CASE_NEW(AddRect);
+        CASE_NEW(AddRoundRect);
+        CASE_DEBUG_RETURN_NIL(Align);
+        CASE_NEW(Animate);
+        // animatebase
+        CASE_NEW(Apply);
+        CASE_DEBUG_RETURN_NIL(ApplyMode);
+        CASE_DEBUG_RETURN_NIL(ApplyTransition);
+        CASE_DISPLAY_NEW(Array);
+        // argb
+        // base64
+        // basebitmap
+        // baseclassinfo
+        CASE_DRAW_NEW(Bitmap);
+        // bitmapencoding
+        // bitmapformat
+        CASE_DRAW_NEW(BitmapShader);
+        CASE_DRAW_NEW(Blur);
+        CASE_DISPLAY_NEW(Boolean);
+        // boundable
+        CASE_DISPLAY_NEW(Bounds);
+        CASE_DEBUG_RETURN_NIL(Cap);
+        CASE_NEW(Clear);
+        CASE_DRAW_NEW(Clip);
+        CASE_NEW(Close);
+        CASE_DRAW_NEW(Color);
+        CASE_NEW(CubicTo);
+        CASE_NEW(Dash);
+        CASE_NEW(DataInput);
+        CASE_NEW(Discrete);
+        // displayable
+        // drawable
+        CASE_NEW(DrawTo);
+        CASE_NEW(Dump);
+        // dynamicstring
+        CASE_DRAW_NEW(Emboss);
+        CASE_DISPLAY_NEW(Event);
+        CASE_DEBUG_RETURN_NIL(EventCode);
+        CASE_DEBUG_RETURN_NIL(EventKind);
+        CASE_DEBUG_RETURN_NIL(EventMode);
+        // filltype
+        // filtertype
+        CASE_DISPLAY_NEW(Float);
+        CASE_NEW(FromPath);
+        CASE_DEBUG_RETURN_NIL(FromPathMode);
+        CASE_NEW(Full);
+        // gradient
+        CASE_NEW(Group);
+        CASE_NEW(HitClear);
+        CASE_NEW(HitTest);
+        CASE_NEW(Image);
+        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_DRAW_NEW(MaskFilter);
+        CASE_DEBUG_RETURN_NIL(MaskFilterBlurStyle);
+        // maskfilterlight
+        CASE_DRAW_NEW(Matrix);
+        // memberfunction
+        // memberproperty
+        CASE_NEW(Move);
+        CASE_NEW(MoveTo);
+        CASE_DISPLAY_NEW(Movie);
+        // msec
+        CASE_NEW(Oval);
+        CASE_DRAW_NEW(Paint);
+        CASE_DRAW_NEW(Path);
+        // pathdirection
+        CASE_DRAW_NEW(PathEffect);
+        // point
+        CASE_NEW(DrawPoint);
+        CASE_NEW(PolyToPoly);
+        CASE_NEW(Polygon);
+        CASE_NEW(Polyline);
+        CASE_NEW(Post);
+        CASE_NEW(QuadTo);
+        CASE_NEW(RCubicTo);
+        CASE_NEW(RLineTo);
+        CASE_NEW(RMoveTo);
+        CASE_NEW(RQuadTo);
+        CASE_NEW(RadialGradient);
+        CASE_DISPLAY_NEW(Random);
+        CASE_DRAW_NEW(Rect);
+        CASE_NEW(RectToRect);
+        CASE_NEW(Remove);
+        CASE_NEW(Replace);
+        CASE_NEW(Rotate);
+        CASE_NEW(RoundRect);
+        CASE_NEW(Save);
+        CASE_NEW(SaveLayer);
+        CASE_NEW(Scale);
+        // screenplay
+        CASE_NEW(Set);
+        CASE_DRAW_NEW(Shader);
+        CASE_NEW(Skew);
+        CASE_NEW(3D_Camera);
+        CASE_NEW(3D_Patch);
+        // 3dpoint
+        CASE_NEW(Snapshot);
+        CASE_DISPLAY_NEW(String);
+        // style
+        CASE_NEW(Text);
+        CASE_DRAW_NEW(TextBox);
+        // textboxalign
+        // textboxmode
+        CASE_NEW(TextOnPath);
+        CASE_NEW(TextToPath);
+        CASE_DEBUG_RETURN_NIL(TileMode);
+        CASE_NEW(Translate);
+        CASE_DRAW_NEW(TransparentShader);
+        CASE_DRAW_NEW(Typeface);
+        CASE_DEBUG_RETURN_NIL(Xfermode);
+        default:
+            SkExtras** end = maker->fExtras.end();
+            for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+                if ((result = (*extraPtr)->createInstance(type)) != NULL)
+                    return result;
+            }
+            SkASSERT(0);
+    }
+    return result;
+}
+
+#undef CASE_NEW
+#undef CASE_DRAW_NEW
+#undef CASE_DISPLAY_NEW
+
+#if SK_USE_CONDENSED_INFO == 0
+
+#define CASE_GET_INFO(_class) case SkType_##_class: \
+    info = Sk##_class::fInfo; infoCount = Sk##_class::fInfoCount; break
+#define CASE_GET_DRAW_INFO(_class) case SkType_##_class: \
+    info = SkDraw##_class::fInfo; infoCount = SkDraw##_class::fInfoCount; break
+#define CASE_GET_DISPLAY_INFO(_class) case SkType_##_class: \
+    info = SkDisplay##_class::fInfo; infoCount = SkDisplay##_class::fInfoCount; \
+    break
+
+const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* maker, 
+        SkDisplayTypes type, int* infoCountPtr) {
+    const SkMemberInfo* info = NULL;
+    int infoCount = 0;
+    switch (type) {
+        // unknown
+        CASE_GET_DISPLAY_INFO(Math);
+        CASE_GET_DISPLAY_INFO(Number);
+        CASE_GET_INFO(Add);
+        CASE_GET_INFO(AddCircle);
+        CASE_GET_INFO(AddGeom);
+        // addmode
+        CASE_GET_INFO(AddOval);
+        CASE_GET_INFO(AddPath);
+        CASE_GET_INFO(AddRect);
+        CASE_GET_INFO(AddRoundRect);
+        // align
+        CASE_GET_INFO(Animate);
+        CASE_GET_INFO(AnimateBase);
+        CASE_GET_INFO(Apply);
+        // applymode
+        // applytransition
+        CASE_GET_DISPLAY_INFO(Array);
+        // argb
+        // base64
+        CASE_GET_INFO(BaseBitmap);
+        // baseclassinfo
+        CASE_GET_DRAW_INFO(Bitmap);
+        // bitmapencoding
+        // bitmapformat
+        CASE_GET_DRAW_INFO(BitmapShader);
+        CASE_GET_DRAW_INFO(Blur);
+        CASE_GET_DISPLAY_INFO(Boolean);
+        // boundable
+        CASE_GET_DISPLAY_INFO(Bounds);
+        // cap
+        // clear
+        CASE_GET_DRAW_INFO(Clip);
+        // close
+        CASE_GET_DRAW_INFO(Color);
+        CASE_GET_INFO(CubicTo);
+        CASE_GET_INFO(Dash);
+        CASE_GET_INFO(DataInput);
+        CASE_GET_INFO(Discrete);
+        // displayable
+        // drawable
+        CASE_GET_INFO(DrawTo);
+        CASE_GET_INFO(Dump);
+        // dynamicstring
+        CASE_GET_DRAW_INFO(Emboss);
+        CASE_GET_DISPLAY_INFO(Event);
+        // eventcode
+        // eventkind
+        // eventmode
+        // filltype
+        // filtertype
+        CASE_GET_DISPLAY_INFO(Float);
+        CASE_GET_INFO(FromPath);
+        // frompathmode
+        // full
+        CASE_GET_INFO(Gradient);
+        CASE_GET_INFO(Group);
+        CASE_GET_INFO(HitClear);
+        CASE_GET_INFO(HitTest);
+        CASE_GET_INFO(Image);
+        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);
+        // maskfilter
+        // maskfilterblurstyle
+        // maskfilterlight
+        CASE_GET_DRAW_INFO(Matrix);
+        // memberfunction
+        // memberproperty
+        CASE_GET_INFO(Move);
+        CASE_GET_INFO(MoveTo);
+        CASE_GET_DISPLAY_INFO(Movie);
+        // msec
+        CASE_GET_INFO(Oval);
+        CASE_GET_DRAW_INFO(Path);
+        CASE_GET_DRAW_INFO(Paint);
+        // pathdirection
+        // patheffect
+        case SkType_Point: info = Sk_Point::fInfo; infoCount = Sk_Point::fInfoCount; break; // no virtual flavor
+        CASE_GET_INFO(DrawPoint); // virtual flavor
+        CASE_GET_INFO(PolyToPoly);
+        CASE_GET_INFO(Polygon);
+        CASE_GET_INFO(Polyline);
+        CASE_GET_INFO(Post);
+        CASE_GET_INFO(QuadTo);
+        CASE_GET_INFO(RCubicTo);
+        CASE_GET_INFO(RLineTo);
+        CASE_GET_INFO(RMoveTo);
+        CASE_GET_INFO(RQuadTo);
+        CASE_GET_INFO(RadialGradient);
+        CASE_GET_DISPLAY_INFO(Random);
+        CASE_GET_DRAW_INFO(Rect);
+        CASE_GET_INFO(RectToRect);
+        CASE_GET_INFO(Remove);
+        CASE_GET_INFO(Replace);
+        CASE_GET_INFO(Rotate);
+        CASE_GET_INFO(RoundRect);
+        CASE_GET_INFO(Save);
+        CASE_GET_INFO(SaveLayer);
+        CASE_GET_INFO(Scale);
+        // screenplay
+        CASE_GET_INFO(Set);
+        CASE_GET_DRAW_INFO(Shader);
+        CASE_GET_INFO(Skew);
+        CASE_GET_INFO(3D_Camera);
+        CASE_GET_INFO(3D_Patch);
+        CASE_GET_INFO(3D_Point);
+        CASE_GET_INFO(Snapshot);
+        CASE_GET_DISPLAY_INFO(String);
+        // style
+        CASE_GET_INFO(Text);
+        CASE_GET_DRAW_INFO(TextBox);
+        // textboxalign
+        // textboxmode
+        CASE_GET_INFO(TextOnPath);
+        CASE_GET_INFO(TextToPath);
+        // tilemode
+        CASE_GET_INFO(Translate);
+        // transparentshader
+        CASE_GET_DRAW_INFO(Typeface);
+        // xfermode
+        // knumberoftypes
+        default: 
+            if (maker) {
+                SkExtras** end = maker->fExtras.end();
+                for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+                    if ((info = (*extraPtr)->getMembers(type, infoCountPtr)) != NULL)
+                        return info;
+                }
+            }
+            return NULL;
+    }
+    if (infoCountPtr)
+        *infoCountPtr = infoCount;
+    return info;
+}
+
+const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* maker, 
+        SkDisplayTypes type, const char** matchPtr ) {
+    int infoCount;
+    const SkMemberInfo* info = GetMembers(maker, type, &infoCount);
+    info = SkMemberInfo::Find(info, infoCount, matchPtr);
+//  SkASSERT(info);
+    return info;
+}
+
+#undef CASE_GET_INFO
+#undef CASE_GET_DRAW_INFO
+#undef CASE_GET_DISPLAY_INFO
+
+#endif // SK_USE_CONDENSED_INFO == 0
+
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    #define DRAW_NAME(_name, _type) {_name, _type, true, false }
+    #define DISPLAY_NAME(_name, _type) {_name, _type, false, true }
+    #define INIT_BOOL_FIELDS    , false, false
+#else
+    #define DRAW_NAME(_name, _type) {_name, _type }
+    #define DISPLAY_NAME(_name, _type) {_name, _type }
+    #define INIT_BOOL_FIELDS
+#endif
+
+const TypeNames gTypeNames[] = {
+    // unknown
+    { "Math", SkType_Math                       INIT_BOOL_FIELDS },
+    { "Number", SkType_Number                   INIT_BOOL_FIELDS },
+    { "add", SkType_Add                         INIT_BOOL_FIELDS },
+    { "addCircle", SkType_AddCircle             INIT_BOOL_FIELDS },
+    // addgeom
+    // addmode
+    { "addOval", SkType_AddOval                 INIT_BOOL_FIELDS },
+    { "addPath", SkType_AddPath                 INIT_BOOL_FIELDS },
+    { "addRect", SkType_AddRect                 INIT_BOOL_FIELDS },
+    { "addRoundRect", SkType_AddRoundRect       INIT_BOOL_FIELDS },
+    // align
+    { "animate", SkType_Animate                 INIT_BOOL_FIELDS },
+    // animateBase
+    { "apply", SkType_Apply                     INIT_BOOL_FIELDS },
+    // applymode
+    // applytransition
+    { "array", SkType_Array                     INIT_BOOL_FIELDS },
+    // argb
+    // base64
+    // basebitmap
+    // baseclassinfo
+    DRAW_NAME("bitmap", SkType_Bitmap),
+    // bitmapencoding
+    // bitmapformat
+    DRAW_NAME("bitmapShader", SkType_BitmapShader),
+    DRAW_NAME("blur", SkType_Blur),
+    { "boolean", SkType_Boolean                 INIT_BOOL_FIELDS },
+    // boundable
+    DISPLAY_NAME("bounds", SkType_Bounds),
+    // cap
+    { "clear", SkType_Clear                     INIT_BOOL_FIELDS },
+    DRAW_NAME("clip", SkType_Clip),
+    { "close", SkType_Close                     INIT_BOOL_FIELDS },
+    DRAW_NAME("color", SkType_Color),
+    { "cubicTo", SkType_CubicTo                 INIT_BOOL_FIELDS },
+    { "dash", SkType_Dash                       INIT_BOOL_FIELDS },
+    { "data", SkType_DataInput                  INIT_BOOL_FIELDS },
+    { "discrete", SkType_Discrete               INIT_BOOL_FIELDS },
+    // displayable
+    // drawable
+    { "drawTo", SkType_DrawTo                   INIT_BOOL_FIELDS },
+    { "dump", SkType_Dump                       INIT_BOOL_FIELDS },
+    // dynamicstring
+    DRAW_NAME("emboss", SkType_Emboss),
+    DISPLAY_NAME("event", SkType_Event),
+    // eventcode
+    // eventkind
+    // eventmode
+    // filltype
+    // filtertype
+    { "float", SkType_Float                     INIT_BOOL_FIELDS },
+    { "fromPath", SkType_FromPath               INIT_BOOL_FIELDS },
+    // frompathmode
+    { "full", SkType_Full                       INIT_BOOL_FIELDS },
+    // gradient
+    { "group", SkType_Group                     INIT_BOOL_FIELDS },
+    { "hitClear", SkType_HitClear               INIT_BOOL_FIELDS },
+    { "hitTest", SkType_HitTest                 INIT_BOOL_FIELDS },
+    { "image", SkType_Image                     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 },
+    { "maskFilter", SkType_MaskFilter           INIT_BOOL_FIELDS },
+    // maskfilterblurstyle
+    // maskfilterlight
+    DRAW_NAME("matrix", SkType_Matrix),
+    // memberfunction
+    // memberproperty
+    { "move", SkType_Move                       INIT_BOOL_FIELDS },
+    { "moveTo", SkType_MoveTo                   INIT_BOOL_FIELDS },
+    { "movie", SkType_Movie                     INIT_BOOL_FIELDS },
+    // msec
+    { "oval", SkType_Oval                       INIT_BOOL_FIELDS },
+    DRAW_NAME("paint", SkType_Paint),
+    DRAW_NAME("path", SkType_Path),
+    // pathdirection
+    { "pathEffect", SkType_PathEffect           INIT_BOOL_FIELDS },
+    // point
+    DRAW_NAME("point", SkType_DrawPoint),
+    { "polyToPoly", SkType_PolyToPoly           INIT_BOOL_FIELDS },
+    { "polygon", SkType_Polygon                 INIT_BOOL_FIELDS },
+    { "polyline", SkType_Polyline               INIT_BOOL_FIELDS },
+    { "post", SkType_Post                       INIT_BOOL_FIELDS },
+    { "quadTo", SkType_QuadTo                   INIT_BOOL_FIELDS },
+    { "rCubicTo", SkType_RCubicTo               INIT_BOOL_FIELDS },
+    { "rLineTo", SkType_RLineTo                 INIT_BOOL_FIELDS },
+    { "rMoveTo", SkType_RMoveTo                 INIT_BOOL_FIELDS },
+    { "rQuadTo", SkType_RQuadTo                 INIT_BOOL_FIELDS },
+    { "radialGradient", SkType_RadialGradient   INIT_BOOL_FIELDS },
+    DISPLAY_NAME("random", SkType_Random),
+    { "rect", SkType_Rect                       INIT_BOOL_FIELDS },
+    { "rectToRect", SkType_RectToRect           INIT_BOOL_FIELDS },
+    { "remove", SkType_Remove                   INIT_BOOL_FIELDS },
+    { "replace", SkType_Replace                 INIT_BOOL_FIELDS },
+    { "rotate", SkType_Rotate                   INIT_BOOL_FIELDS },
+    { "roundRect", SkType_RoundRect             INIT_BOOL_FIELDS },
+    { "save", SkType_Save                       INIT_BOOL_FIELDS },
+    { "saveLayer", SkType_SaveLayer             INIT_BOOL_FIELDS },
+    { "scale", SkType_Scale                     INIT_BOOL_FIELDS },
+    // screenplay
+    { "set", SkType_Set                         INIT_BOOL_FIELDS },
+    { "shader", SkType_Shader                   INIT_BOOL_FIELDS },
+    { "skew", SkType_Skew                       INIT_BOOL_FIELDS },
+    { "skia3d:camera", SkType_3D_Camera         INIT_BOOL_FIELDS },
+    { "skia3d:patch", SkType_3D_Patch           INIT_BOOL_FIELDS },
+    // point
+    { "snapshot", SkType_Snapshot               INIT_BOOL_FIELDS },
+    { "string", SkType_String                   INIT_BOOL_FIELDS },
+    // style
+    { "text", SkType_Text                       INIT_BOOL_FIELDS },
+    { "textBox", SkType_TextBox                 INIT_BOOL_FIELDS },
+    // textboxalign
+    // textboxmode
+    { "textOnPath", SkType_TextOnPath           INIT_BOOL_FIELDS },
+    { "textToPath", SkType_TextToPath           INIT_BOOL_FIELDS },
+    // tilemode
+    { "translate", SkType_Translate             INIT_BOOL_FIELDS },
+    DRAW_NAME("transparentShader", SkType_TransparentShader),
+    { "typeface", SkType_Typeface               INIT_BOOL_FIELDS }
+    // xfermode
+    // knumberoftypes
+};
+
+const int kTypeNamesSize = SK_ARRAY_COUNT(gTypeNames);
+
+SkDisplayTypes SkDisplayType::Find(SkAnimateMaker* maker, const SkMemberInfo* match) {
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        SkDisplayTypes type = gTypeNames[index].fType;
+        const SkMemberInfo* info = SkDisplayType::GetMembers(maker, type, NULL);
+        if (info == match)
+            return type;
+    }
+    return (SkDisplayTypes) -1;
+}
+
+// !!! optimize this by replacing function with a byte-sized lookup table
+SkDisplayTypes SkDisplayType::GetParent(SkAnimateMaker* maker, SkDisplayTypes base) {
+    if (base == SkType_Group || base == SkType_Save || base == SkType_SaveLayer)        //!!! cheat a little until we have a lookup table
+        return SkType_Displayable;
+    if (base == SkType_Set)
+        return SkType_Animate;  // another cheat until we have a lookup table
+    const SkMemberInfo* info = GetMembers(maker, base, NULL); // get info for this type
+    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 
+    // this (and table builder) could know type without the following steps:
+    const SkMemberInfo* inherited = info->getInherited();
+    SkDisplayTypes result = (SkDisplayTypes) (SkType_Unknown + 1);
+    for (; result <= SkType_Xfermode; result = (SkDisplayTypes) (result + 1)) {
+        const SkMemberInfo* match = GetMembers(maker, result, NULL);
+        if (match == inherited)
+            break;
+    }
+    SkASSERT(result <= SkType_Xfermode);
+    return result;
+}
+
+SkDisplayTypes SkDisplayType::GetType(SkAnimateMaker* maker, const char match[], size_t len ) {
+    int index = SkStrSearch(&gTypeNames[0].fName, kTypeNamesSize, match, 
+        len, sizeof(gTypeNames[0]));
+    if (index >= 0 && index < kTypeNamesSize)
+        return gTypeNames[index].fType;
+    SkExtras** end = maker->fExtras.end();
+    for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+        SkDisplayTypes result = (*extraPtr)->getType(match, len);
+        if (result != SkType_Unknown)
+            return result;
+    }
+    return (SkDisplayTypes) -1;
+}
+
+bool SkDisplayType::IsEnum(SkAnimateMaker* , SkDisplayTypes type) {
+    switch (type) {
+        case SkType_AddMode:
+        case SkType_Align:
+        case SkType_ApplyMode:
+        case SkType_ApplyTransition:
+        case SkType_BitmapEncoding:
+        case SkType_BitmapFormat:
+        case SkType_Boolean:
+        case SkType_Cap:
+        case SkType_EventCode:
+        case SkType_EventKind:
+        case SkType_EventMode:
+        case SkType_FillType:
+        case SkType_FilterType:
+        case SkType_FontStyle:
+        case SkType_FromPathMode:
+        case SkType_Join:
+        case SkType_MaskFilterBlurStyle:
+        case SkType_PathDirection:
+        case SkType_Style:
+        case SkType_TextBoxAlign:
+        case SkType_TextBoxMode:
+        case SkType_TileMode:
+        case SkType_Xfermode:
+            return true;
+        default:    // to avoid warnings
+            break;
+    }
+    return false;
+}
+
+bool SkDisplayType::IsDisplayable(SkAnimateMaker* , SkDisplayTypes type) {
+    switch (type) {
+        case SkType_Add:
+        case SkType_AddCircle:
+        case SkType_AddOval:
+        case SkType_AddPath:
+        case SkType_AddRect:
+        case SkType_AddRoundRect:
+        case SkType_Animate:
+        case SkType_AnimateBase:
+        case SkType_Apply:
+        case SkType_BaseBitmap:
+        case SkType_Bitmap:
+        case SkType_BitmapShader:
+        case SkType_Blur:
+        case SkType_Clear:
+        case SkType_Clip:
+        case SkType_Close:
+        case SkType_Color:
+        case SkType_CubicTo:
+        case SkType_Dash:
+        case SkType_DataInput:
+        case SkType_Discrete:
+        case SkType_Displayable:
+        case SkType_Drawable:
+        case SkType_DrawTo:
+        case SkType_Emboss:
+        case SkType_Event:
+        case SkType_FromPath:
+        case SkType_Full:
+        case SkType_Group:
+        case SkType_Image:
+        case SkType_Input:
+        case SkType_Line:
+        case SkType_LineTo:
+        case SkType_LinearGradient:
+        case SkType_Matrix:
+        case SkType_Move:
+        case SkType_MoveTo:
+        case SkType_Movie:
+        case SkType_Oval:
+        case SkType_Paint:
+        case SkType_Path:
+        case SkType_PolyToPoly:
+        case SkType_Polygon:
+        case SkType_Polyline:
+        case SkType_Post:
+        case SkType_QuadTo:
+        case SkType_RCubicTo:
+        case SkType_RLineTo:
+        case SkType_RMoveTo:
+        case SkType_RQuadTo:
+        case SkType_RadialGradient:
+        case SkType_Random:
+        case SkType_Rect:
+        case SkType_RectToRect:
+        case SkType_Remove:
+        case SkType_Replace:
+        case SkType_Rotate:
+        case SkType_RoundRect:
+        case SkType_Save:
+        case SkType_SaveLayer:
+        case SkType_Scale:
+        case SkType_Set:
+        case SkType_Shader:
+        case SkType_Skew:
+        case SkType_3D_Camera:
+        case SkType_3D_Patch:
+        case SkType_Snapshot:
+        case SkType_Text:
+        case SkType_TextBox:
+        case SkType_TextOnPath:
+        case SkType_TextToPath:
+        case SkType_Translate:
+        case SkType_TransparentShader:
+            return true;
+        default:    // to avoid warnings
+            break;
+    }
+    return false;
+}
+
+bool SkDisplayType::IsStruct(SkAnimateMaker* , SkDisplayTypes type) {
+    switch (type) {
+        case SkType_Point:
+        case SkType_3D_Point:
+            return true;
+        default:    // to avoid warnings
+            break;
+    }
+    return false;
+}
+
+
+SkDisplayTypes SkDisplayType::RegisterNewType() {
+    gNewTypes = (SkDisplayTypes) (gNewTypes + 1);
+    return gNewTypes;
+}
+
+
+
+#ifdef SK_DEBUG
+const char* SkDisplayType::GetName(SkAnimateMaker* maker, SkDisplayTypes type) {
+    for (int index = 0; index < kTypeNamesSize - 1; index++) {
+        if (gTypeNames[index].fType == type)
+            return gTypeNames[index].fName;
+    }
+    SkExtras** end = maker->fExtras.end();
+    for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+        const char* result = (*extraPtr)->getName(type);
+        if (result != NULL)
+            return result;
+    }
+    return NULL;
+}
+#endif
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkDisplayType::UnitTest() {
+    SkAnimator animator;
+    SkAnimateMaker* maker = animator.fMaker;
+    int index;
+    for (index = 0; index < kTypeNamesSize - 1; index++) {
+        SkASSERT(strcmp(gTypeNames[index].fName, gTypeNames[index + 1].fName) < 0);
+        SkASSERT(gTypeNames[index].fType < gTypeNames[index + 1].fType);
+    }
+    for (index = 0; index < kTypeNamesSize; index++) {
+        SkDisplayable* test = CreateInstance(maker, gTypeNames[index].fType);
+        if (test == NULL)
+            continue;
+#if defined _WIN32 && _MSC_VER >= 1300  && defined _INC_CRTDBG // only on windows, only if using "crtdbg.h"
+    // we know that crtdbg puts 0xfdfdfdfd at the end of the block
+    // look for unitialized memory, signature 0xcdcdcdcd prior to that
+        int* start = (int*) test;
+        while (*start != 0xfdfdfdfd) {
+            SkASSERT(*start != 0xcdcdcdcd);
+            start++;
+        }
+#endif
+        delete test;
+    }
+    for (index = 0; index < kTypeNamesSize; index++) {
+        int infoCount;
+        const SkMemberInfo* info = GetMembers(maker, gTypeNames[index].fType, &infoCount);
+        if (info == NULL)
+            continue;
+#if SK_USE_CONDENSED_INFO == 0
+        for (int inner = 0; inner < infoCount - 1; inner++) {
+            if (info[inner].fType == SkType_BaseClassInfo)
+                continue;
+            SkASSERT(strcmp(info[inner].fName, info[inner + 1].fName) < 0);
+        }
+#endif
+    }
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    BuildCondensedInfo(maker);
+#endif
+}
+#endif
diff --git a/legacy/src/animator/SkDisplayType.h b/legacy/src/animator/SkDisplayType.h
new file mode 100644
index 0000000..fe82b31
--- /dev/null
+++ b/legacy/src/animator/SkDisplayType.h
@@ -0,0 +1,208 @@
+
+/*
+ * 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 SkDisplayType_DEFINED
+#define SkDisplayType_DEFINED
+
+#include "SkMath.h"
+#include "SkScalar.h"
+
+#ifdef SK_DEBUG
+    #ifdef SK_CAN_USE_FLOAT
+        #define SK_DUMP_ENABLED
+    #endif
+    #ifdef SK_BUILD_FOR_MAC
+        #define SK_FIND_LEAKS
+    #endif
+#endif
+
+#define SK_LITERAL_STR_EQUAL(str, token, len) (sizeof(str) - 1 == len \
+    && strncmp(str, token, sizeof(str) - 1) == 0)
+
+class SkAnimateMaker;
+class SkDisplayable;
+struct SkMemberInfo;
+
+enum SkDisplayTypes {
+    SkType_Unknown,
+    SkType_Math, // for ecmascript compatible Math functions and constants
+    SkType_Number,  // for for ecmascript compatible Number functions and constants
+    SkType_Add,
+    SkType_AddCircle,
+    SkType_AddGeom,
+    SkType_AddMode,
+    SkType_AddOval,
+    SkType_AddPath,
+    SkType_AddRect, // path part
+    SkType_AddRoundRect,
+    SkType_Align,
+    SkType_Animate,
+    SkType_AnimateBase, // base type for animate, set
+    SkType_Apply,
+    SkType_ApplyMode,
+    SkType_ApplyTransition,
+    SkType_Array,
+    SkType_ARGB,
+    SkType_Base64,
+    SkType_BaseBitmap,
+    SkType_BaseClassInfo,
+    SkType_Bitmap,
+    SkType_BitmapEncoding,
+    SkType_BitmapFormat,
+    SkType_BitmapShader,
+    SkType_Blur,
+    SkType_Boolean, // can have values -1 (uninitialized), 0, 1
+    SkType_Boundable,
+    SkType_Bounds,
+    SkType_Cap,
+    SkType_Clear,
+    SkType_Clip,
+    SkType_Close,
+    SkType_Color,
+    SkType_CubicTo,
+    SkType_Dash,
+    SkType_DataInput,
+    SkType_Discrete,
+    SkType_Displayable,
+    SkType_Drawable,
+    SkType_DrawTo,
+    SkType_Dump,
+    SkType_DynamicString,   // evaluate at draw time
+    SkType_Emboss,
+    SkType_Event,
+    SkType_EventCode,
+    SkType_EventKind,
+    SkType_EventMode,
+    SkType_FillType,
+    SkType_FilterType,
+    SkType_Float,
+    SkType_FontStyle,
+    SkType_FromPath,
+    SkType_FromPathMode,
+    SkType_Full,
+    SkType_Gradient,
+    SkType_Group,
+    SkType_HitClear,
+    SkType_HitTest,
+    SkType_Image,
+    SkType_Include,
+    SkType_Input,
+    SkType_Int,
+    SkType_Join,
+    SkType_Line, // simple line primitive
+    SkType_LineTo, // used as part of path construction
+    SkType_LinearGradient,
+    SkType_MaskFilter,
+    SkType_MaskFilterBlurStyle,
+    SkType_MaskFilterLight,
+    SkType_Matrix,
+    SkType_MemberFunction,
+    SkType_MemberProperty,
+    SkType_Move,
+    SkType_MoveTo,
+    SkType_Movie,
+    SkType_MSec,
+    SkType_Oval,
+    SkType_Paint,
+    SkType_Path,
+    SkType_PathDirection,
+    SkType_PathEffect,
+    SkType_Point,   // used inside other structures, no vtable
+    SkType_DrawPoint, // used to draw points, has a vtable
+    SkType_PolyToPoly,
+    SkType_Polygon,
+    SkType_Polyline,
+    SkType_Post,
+    SkType_QuadTo,
+    SkType_RCubicTo,
+    SkType_RLineTo,
+    SkType_RMoveTo,
+    SkType_RQuadTo,
+    SkType_RadialGradient,
+    SkType_Random,
+    SkType_Rect,
+    SkType_RectToRect,
+    SkType_Remove,
+    SkType_Replace,
+    SkType_Rotate,
+    SkType_RoundRect,
+    SkType_Save,
+    SkType_SaveLayer,
+    SkType_Scale,
+    SkType_Screenplay,
+    SkType_Set,
+    SkType_Shader,
+    SkType_Skew,
+    SkType_3D_Camera,
+    SkType_3D_Patch,
+    SkType_3D_Point,
+    SkType_Snapshot,
+    SkType_String,  // pointer to SkString
+    SkType_Style,
+    SkType_Text,
+    SkType_TextBox,
+    SkType_TextBoxAlign,
+    SkType_TextBoxMode,
+    SkType_TextOnPath,
+    SkType_TextToPath,
+    SkType_TileMode,
+    SkType_Translate,
+    SkType_TransparentShader,
+    SkType_Typeface,
+    SkType_Xfermode,
+    kNumberOfTypes
+};
+
+struct TypeNames {
+    const char* fName;
+    SkDisplayTypes fType;
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    bool fDrawPrefix;
+    bool fDisplayPrefix;
+#endif
+};
+
+#ifdef SK_DEBUG
+typedef SkDisplayTypes SkFunctionParamType;
+#else
+typedef unsigned char SkFunctionParamType;
+#endif
+
+extern const TypeNames gTypeNames[];
+extern const int kTypeNamesSize;
+
+class SkDisplayType {
+public:
+    static SkDisplayTypes Find(SkAnimateMaker* , const SkMemberInfo* );
+    static const SkMemberInfo* GetMember(SkAnimateMaker* , SkDisplayTypes , const char** );
+    static const SkMemberInfo* GetMembers(SkAnimateMaker* , SkDisplayTypes , int* infoCountPtr);
+    static SkDisplayTypes GetParent(SkAnimateMaker* , SkDisplayTypes );
+    static bool IsDisplayable(SkAnimateMaker* , SkDisplayTypes );
+    static bool IsEnum(SkAnimateMaker* , SkDisplayTypes );
+    static bool IsStruct(SkAnimateMaker* , SkDisplayTypes );
+    static SkDisplayTypes RegisterNewType();
+    static SkDisplayTypes Resolve(const char[] , const SkMemberInfo** );
+#ifdef SK_DEBUG
+    static bool IsAnimate(SkDisplayTypes type ) { return type == SkType_Animate || 
+        type == SkType_Set; }
+    static const char* GetName(SkAnimateMaker* , SkDisplayTypes );
+#endif
+#ifdef SK_SUPPORT_UNITTEST
+    static void UnitTest();
+#endif
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    static void BuildCondensedInfo(SkAnimateMaker* );
+#endif
+    static SkDisplayTypes GetType(SkAnimateMaker* , const char[] , size_t len);
+    static SkDisplayable* CreateInstance(SkAnimateMaker* , SkDisplayTypes );
+private:
+    static SkDisplayTypes gNewTypes;
+};
+
+#endif // SkDisplayType_DEFINED
diff --git a/legacy/src/animator/SkDisplayTypes.cpp b/legacy/src/animator/SkDisplayTypes.cpp
new file mode 100644
index 0000000..6132eb7
--- /dev/null
+++ b/legacy/src/animator/SkDisplayTypes.cpp
@@ -0,0 +1,221 @@
+
+/*
+ * 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 "SkDisplayTypes.h"
+#include "SkAnimateBase.h"
+
+bool SkDisplayDepend::canContainDependents() const {
+    return true; 
+}
+
+void SkDisplayDepend::dirty() {
+    SkDisplayable** last = fDependents.end();
+    for (SkDisplayable** depPtr = fDependents.begin(); depPtr < last; depPtr++) {
+        SkAnimateBase* animate = (SkAnimateBase* ) *depPtr;
+        animate->setChanged(true);
+    }
+}
+
+// Boolean
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayBoolean::fInfo[] = {
+    SK_MEMBER(value, Boolean)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayBoolean);
+
+SkDisplayBoolean::SkDisplayBoolean() : value(false) {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayBoolean::dump(SkAnimateMaker* maker){
+    dumpBase(maker);
+    SkDebugf("value=\"%s\" />\n", value ? "true" : "false");
+}
+#endif
+
+// int32_t
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayInt::fInfo[] = {
+    SK_MEMBER(value, Int)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayInt);
+
+SkDisplayInt::SkDisplayInt() : value(0) {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayInt::dump(SkAnimateMaker* maker){
+    dumpBase(maker);
+    SkDebugf("value=\"%d\" />\n", value);
+}
+#endif
+
+// SkScalar
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayFloat::fInfo[] = {
+    SK_MEMBER(value, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayFloat);
+
+SkDisplayFloat::SkDisplayFloat() : value(0) {
+}
+
+#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
+
+// SkString
+enum SkDisplayString_Functions {
+    SK_FUNCTION(slice)
+};
+
+enum SkDisplayString_Properties {
+    SK_PROPERTY(length)
+};
+
+const SkFunctionParamType SkDisplayString::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_Int,   // slice
+    (SkFunctionParamType) SkType_Int,
+    (SkFunctionParamType) 0
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayString::fInfo[] = {
+    SK_MEMBER_PROPERTY(length, Int),
+    SK_MEMBER_FUNCTION(slice, String),
+    SK_MEMBER(value, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayString);
+
+SkDisplayString::SkDisplayString() {
+}
+
+SkDisplayString::SkDisplayString(SkString& copyFrom) : value(copyFrom) {
+}
+
+void SkDisplayString::executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* scriptValue) {
+    if (scriptValue == NULL)
+        return;
+    SkASSERT(target == this);
+    switch (index) {
+        case SK_FUNCTION(slice):
+            scriptValue->fType = SkType_String;
+            SkASSERT(parameters[0].fType == SkType_Int);
+            int start =  parameters[0].fOperand.fS32;
+            if (start < 0)
+                start = (int) (value.size() - start);
+            int end = (int) value.size();
+            if (parameters.count() > 1) {
+                SkASSERT(parameters[1].fType == SkType_Int);
+                end = parameters[1].fOperand.fS32;
+            }
+            //if (end >= 0 && end < (int) value.size())
+            if (end >= 0 && end <= (int) value.size())
+                scriptValue->fOperand.fString = new SkString(&value.c_str()[start], end - start);
+            else
+                scriptValue->fOperand.fString = new SkString(value);
+        break;
+    }
+}
+
+const SkFunctionParamType* SkDisplayString::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+bool SkDisplayString::getProperty(int index, SkScriptValue* scriptValue) const {
+    switch (index) { 
+        case SK_PROPERTY(length):
+            scriptValue->fType = SkType_Int;
+            scriptValue->fOperand.fS32 = (int32_t) value.size();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+
+// SkArray
+#if 0   // !!! reason enough to qualify enum with class name or move typedArray into its own file
+enum SkDisplayArray_Properties {
+    SK_PROPERTY(length)
+};
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayArray::fInfo[] = {
+    SK_MEMBER_PROPERTY(length, Int),
+    SK_MEMBER_ARRAY(values, Unknown)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayArray);
+
+SkDisplayArray::SkDisplayArray() {
+}
+
+SkDisplayArray::SkDisplayArray(SkTypedArray& copyFrom) : values(copyFrom) {
+
+}
+
+SkDisplayArray::~SkDisplayArray() {
+    if (values.getType() == SkType_String) {
+        for (int index = 0; index < values.count(); index++)
+            delete values[index].fString;
+        return;
+    }
+    if (values.getType() == SkType_Array) {
+        for (int index = 0; index < values.count(); index++)
+            delete values[index].fArray;
+    }
+}
+
+bool SkDisplayArray::getProperty(int index, SkScriptValue* value) const {
+    switch (index) { 
+        case SK_PROPERTY(length):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = values.count();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+
+
diff --git a/legacy/src/animator/SkDisplayTypes.h b/legacy/src/animator/SkDisplayTypes.h
new file mode 100644
index 0000000..75cace6
--- /dev/null
+++ b/legacy/src/animator/SkDisplayTypes.h
@@ -0,0 +1,107 @@
+
+/*
+ * 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 SkDisplayTypes_DEFINED
+#define SkDisplayTypes_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkTypedArray.h"
+
+class SkOpArray; // compiled script experiment
+
+
+class SkDisplayDepend : public SkDisplayable {
+public:
+    virtual bool canContainDependents() const;
+    void addDependent(SkDisplayable* displayable) {
+        if (fDependents.find(displayable) < 0)
+            *fDependents.append() = displayable;
+    }
+    virtual void dirty();
+private:
+    SkTDDisplayableArray fDependents;
+    typedef SkDisplayable INHERITED;
+};
+
+class SkDisplayBoolean : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Boolean);
+    SkDisplayBoolean();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkBool value;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript_Box;
+    friend class SkAnimatorScript_Unbox;
+    typedef SkDisplayDepend INHERITED;
+};
+
+class SkDisplayInt : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Int);
+    SkDisplayInt();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+private:
+    int32_t value;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript_Box;
+    friend class SkAnimatorScript_Unbox;
+    typedef SkDisplayDepend INHERITED;
+};
+
+class SkDisplayFloat : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Float);
+    SkDisplayFloat();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+private:
+    SkScalar value;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript_Box;
+    friend class SkAnimatorScript_Unbox;
+    typedef SkDisplayDepend INHERITED;
+};
+
+class SkDisplayString : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(String);
+    SkDisplayString();
+    SkDisplayString(SkString& );
+    virtual void executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    virtual bool getProperty(int index, SkScriptValue* ) const;
+    SkString value;
+private:
+    static const SkFunctionParamType fFunctionParameters[];
+};
+
+class SkDisplayArray : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Array);
+    SkDisplayArray();
+    SkDisplayArray(SkTypedArray& );
+    SkDisplayArray(SkOpArray& ); // compiled script experiment
+    virtual ~SkDisplayArray();
+    virtual bool getProperty(int index, SkScriptValue* ) const;
+private:
+    SkTypedArray values;
+    friend class SkAnimator;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript2;
+    friend class SkAnimatorScript_Unbox;
+    friend class SkDisplayable;
+    friend struct SkMemberInfo;
+    friend class SkScriptEngine;
+};
+
+#endif // SkDisplayTypes_DEFINED
+
diff --git a/legacy/src/animator/SkDisplayXMLParser.cpp b/legacy/src/animator/SkDisplayXMLParser.cpp
new file mode 100644
index 0000000..68dc259
--- /dev/null
+++ b/legacy/src/animator/SkDisplayXMLParser.cpp
@@ -0,0 +1,318 @@
+
+/*
+ * 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 "SkDisplayXMLParser.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkUtils.h"
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+
+static char const* const gErrorStrings[] = {
+    "unknown error ",
+    "apply scopes itself",
+    "display tree too deep (circular reference?) ",
+    "element missing parent ",
+    "element type not allowed in parent ",
+    "error adding <data> to <post> ",
+    "error adding to <matrix> ",
+    "error adding to <paint> ",
+    "error adding to <path> ",
+    "error in attribute value ",
+    "error in script ",
+    "expected movie in sink attribute ",
+    "field not in target ",
+    "number of offsets in gradient must match number of colors",
+    "no offset in gradient may be greater than one",
+    "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", 
+    "in include ",
+    "in movie ",
+    "include name unknown or missing ",
+    "index out of range ",
+    "movie name unknown or missing ",
+    "no parent available to resolve sink attribute ",
+    "parent element can't contain ",
+    "saveLayer must specify a bounds",
+    "target id not found ",
+    "unexpected type "
+};
+
+SkDisplayXMLParserError::~SkDisplayXMLParserError() {
+}
+
+void SkDisplayXMLParserError::getErrorString(SkString* str) const {
+    if (fCode > kUnknownError)
+        str->set(gErrorStrings[fCode - kUnknownError]);
+    else
+        str->reset();
+    INHERITED::getErrorString(str);
+}
+
+void SkDisplayXMLParserError::setInnerError(SkAnimateMaker* parent, const SkString& src) {
+    SkString inner;
+    getErrorString(&inner);
+    inner.prepend(": ");
+    inner.prependS32(getLineNumber());
+    inner.prepend(", line ");
+    inner.prepend(src);
+    parent->setErrorNoun(inner);
+}
+
+
+SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker)
+    : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude), 
+        fInSkia(maker.fInInclude), fCurrDisplayable(NULL)
+{
+}
+
+SkDisplayXMLParser::~SkDisplayXMLParser() {
+    if (fCurrDisplayable && fMaker.fChildren.find(fCurrDisplayable) < 0)
+        delete fCurrDisplayable;
+    for (Parent* parPtr = fParents.begin() + 1; parPtr < fParents.end(); parPtr++) {
+        SkDisplayable* displayable = parPtr->fDisplayable;
+        if (displayable == fCurrDisplayable)
+            continue;
+        SkASSERT(fMaker.fChildren.find(displayable) < 0);
+        if (fMaker.fHelpers.find(displayable) < 0)
+            delete displayable;
+    }
+}
+
+
+
+bool SkDisplayXMLParser::onAddAttribute(const char name[], const char value[]) {
+    return onAddAttributeLen(name, value, strlen(value));
+}
+
+bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[], 
+                                        size_t attrValueLen)
+{
+    if (fCurrDisplayable == NULL)    // this signals we should ignore attributes for this element
+        return strncmp(attrName, "xmlns", sizeof("xmlns") - 1) != 0;
+    SkDisplayable*  displayable = fCurrDisplayable;
+    SkDisplayTypes  type = fCurrType;
+
+    if (strcmp(attrName, "id") == 0) {
+        if (fMaker.find(attrValue, attrValueLen, NULL)) {
+            fError->setNoun(attrValue, attrValueLen);
+            fError->setCode(SkXMLParserError::kDuplicateIDs);
+            return true;
+        }
+#ifdef SK_DEBUG
+        displayable->_id.set(attrValue, attrValueLen);
+        displayable->id = displayable->_id.c_str();
+#endif
+        fMaker.idsSet(attrValue, attrValueLen, displayable);
+        int parentIndex = fParents.count() - 1;
+        if (parentIndex > 0) {
+            SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
+            parent->setChildHasID();
+        }
+        return false;
+    }
+    const char* name = attrName;
+    const SkMemberInfo* info = SkDisplayType::GetMember(&fMaker, type, &name);
+    if (info == NULL) {
+        fError->setNoun(name);
+        fError->setCode(SkXMLParserError::kUnknownAttributeName);
+        return true;
+    }
+    if (info->setValue(fMaker, NULL, 0, info->getCount(), displayable, info->getType(), attrValue,
+            attrValueLen))
+        return false;
+    if (fMaker.fError.hasError()) {
+        fError->setNoun(attrValue, attrValueLen);
+        return true;
+    }
+    SkDisplayable* ref = NULL;
+    if (fMaker.find(attrValue, attrValueLen, &ref) == false) {
+        ref = fMaker.createInstance(attrValue, attrValueLen);
+        if (ref == NULL) {
+            fError->setNoun(attrValue, attrValueLen);
+            fError->setCode(SkXMLParserError::kErrorInAttributeValue);
+            return true;
+        } else
+            fMaker.helperAdd(ref);
+    }
+    if (info->fType != SkType_MemberProperty) {
+        fError->setNoun(name);
+        fError->setCode(SkXMLParserError::kUnknownAttributeName);
+        return true;
+    }
+    SkScriptValue scriptValue;
+    scriptValue.fOperand.fDisplayable = ref;
+    scriptValue.fType = ref->getType();
+    displayable->setProperty(info->propertyIndex(), scriptValue);
+    return false;
+}
+
+#if defined(SK_BUILD_FOR_WIN32)
+    #define SK_strcasecmp   _stricmp
+    #define SK_strncasecmp  _strnicmp
+#else
+    #define SK_strcasecmp   strcasecmp
+    #define SK_strncasecmp  strncasecmp
+#endif
+
+bool SkDisplayXMLParser::onEndElement(const char elem[])
+{
+    int parentIndex = fParents.count() - 1;
+    if (parentIndex >= 0) {
+        Parent& container = fParents[parentIndex];
+        SkDisplayable* displayable = container.fDisplayable;
+        fMaker.fEndDepth = parentIndex;
+        displayable->onEndElement(fMaker);
+        if (fMaker.fError.hasError()) 
+            return true;
+        if (parentIndex > 0) {
+            SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
+            bool result = parent->add(fMaker, displayable);
+            if (fMaker.hasError()) 
+                return true;
+            if (result == false) {
+                int infoCount;
+                const SkMemberInfo* info = 
+                    SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount);
+                const SkMemberInfo* foundInfo;
+                if ((foundInfo = searchContainer(info, infoCount)) != NULL) {
+                    parent->setReference(foundInfo, displayable);
+        //          if (displayable->isHelper() == false)
+                        fMaker.helperAdd(displayable);
+                } else {
+                    fMaker.setErrorCode(SkDisplayXMLParserError::kElementTypeNotAllowedInParent);
+                    return true;
+                }
+            }
+            if (parent->childrenNeedDisposing())
+                delete displayable;
+        }
+        fParents.remove(parentIndex);
+    }
+    fCurrDisplayable = NULL;
+    if (fInInclude == false && SK_strcasecmp(elem, "screenplay") == 0) {
+        if (fMaker.fInMovie == false) {
+            fMaker.fEnableTime = fMaker.getAppTime();
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            if (fMaker.fDebugTimeBase == (SkMSec) -1)
+                fMaker.fDebugTimeBase = fMaker.fEnableTime;
+            SkString debugOut;
+            SkMSec time = fMaker.getAppTime();
+            debugOut.appendS32(time - fMaker.fDebugTimeBase);
+            debugOut.append(" onLoad enable=");
+            debugOut.appendS32(fMaker.fEnableTime - fMaker.fDebugTimeBase);
+            SkDebugf("%s\n", debugOut.c_str());
+#endif
+            fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL);
+            if (fMaker.fError.hasError()) 
+                return true;
+            fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
+
+        }
+        fInSkia = false;
+    }
+    return false;
+}
+
+bool SkDisplayXMLParser::onStartElement(const char name[])
+{
+    return onStartElementLen(name, strlen(name));
+}
+
+bool SkDisplayXMLParser::onStartElementLen(const char name[], size_t len) {
+    fCurrDisplayable = NULL; // init so we'll ignore attributes if we exit early
+
+    if (SK_strncasecmp(name, "screenplay", len) == 0) {
+        fInSkia = true;
+        if (fInInclude == false)
+            fMaker.idsSet(name, len, &fMaker.fScreenplay);
+        return false;
+    }
+    if (fInSkia == false)
+        return false;
+
+    SkDisplayable* displayable = fMaker.createInstance(name, len);
+    if (displayable == NULL) {
+        fError->setNoun(name, len);
+        fError->setCode(SkXMLParserError::kUnknownElement);
+        return true;
+    }
+    SkDisplayTypes type = displayable->getType();
+    Parent record = { displayable, type };
+    *fParents.append() = record;
+    if (fParents.count() == 1)
+        fMaker.childrenAdd(displayable);
+    else {
+        Parent* parent = fParents.end() - 2;
+        if (displayable->setParent(parent->fDisplayable)) {
+            fError->setNoun(name, len);
+            getError()->setCode(SkDisplayXMLParserError::kParentElementCantContain);
+            return true;
+        }
+    }
+
+    // set these for subsequent calls to addAttribute()
+    fCurrDisplayable = displayable;
+    fCurrType = type;
+    return false;
+}
+
+const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* infoBase,
+                                                         int infoCount) {
+    const SkMemberInfo* bestDisplayable = NULL;
+    const SkMemberInfo* lastResort = NULL;
+    for (int index = 0; index < infoCount; index++) {
+        const SkMemberInfo* info = &infoBase[index];
+        if (info->fType == SkType_BaseClassInfo) {
+            const SkMemberInfo* inherited = info->getInherited();
+            const SkMemberInfo* result = searchContainer(inherited, info->fCount);
+            if (result != NULL)
+                return result;
+            continue;
+        }
+        Parent* container = fParents.end() - 1;
+        SkDisplayTypes type = (SkDisplayTypes) info->fType;
+        if (type == SkType_MemberProperty) 
+            type = info->propertyType();
+        SkDisplayTypes containerType = container->fType;
+        if (type == containerType && (type == SkType_Rect || type == SkType_Polygon ||
+            type == SkType_Array || type == SkType_Int || type == SkType_Bitmap))
+            goto rectNext;
+        while (type != containerType) {
+            if (containerType == SkType_Displayable)
+                goto next;
+            containerType = SkDisplayType::GetParent(&fMaker, containerType);
+            if (containerType == SkType_Unknown)
+                goto next;
+        }
+        return info;
+next:
+        if (type == SkType_Drawable || (type == SkType_Displayable && 
+            container->fDisplayable->isDrawable())) {
+rectNext:
+            if (fParents.count() > 1) {
+                Parent* parent = fParents.end() - 2;
+                if (info == parent->fDisplayable->preferredChild(type))
+                    bestDisplayable = info;
+                else
+                    lastResort = info;
+            }
+        }
+    }
+    if (bestDisplayable)
+        return bestDisplayable;
+    if (lastResort)
+        return lastResort;
+    return NULL;
+}
+
+
diff --git a/legacy/src/animator/SkDisplayXMLParser.h b/legacy/src/animator/SkDisplayXMLParser.h
new file mode 100644
index 0000000..c18e48f
--- /dev/null
+++ b/legacy/src/animator/SkDisplayXMLParser.h
@@ -0,0 +1,93 @@
+
+/*
+ * 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 SkDisplayXMLParser_DEFINED
+#define SkDisplayXMLParser_DEFINED
+
+#include "SkIntArray.h"
+#include "SkTDict.h"
+#include "SkDisplayType.h"
+#include "SkXMLParser.h"
+
+class SkAnimateMaker;
+class SkDisplayable;
+
+class SkDisplayXMLParserError : public SkXMLParserError {
+public:
+    enum ErrorCode {
+        kApplyScopesItself = kUnknownError + 1,
+        kDisplayTreeTooDeep,
+        kElementMissingParent,
+        kElementTypeNotAllowedInParent,
+        kErrorAddingDataToPost,
+        kErrorAddingToMatrix,
+        kErrorAddingToPaint,
+        kErrorAddingToPath,
+        kErrorInAttributeValue,
+        kErrorInScript,
+        kExpectedMovie,
+        kFieldNotInTarget,
+        kGradientOffsetsDontMatchColors,
+        kGradientOffsetsMustBeNoMoreThanOne,
+        kGradientOffsetsMustEndWithOne,
+        kGradientOffsetsMustIncrease,
+        kGradientOffsetsMustStartWithZero,
+        kGradientPointsLengthMustBeFour,
+        kInInclude,
+        kInMovie,
+        kIncludeNameUnknownOrMissing,
+        kIndexOutOfRange,
+        kMovieNameUnknownOrMissing,
+        kNoParentAvailable,
+        kParentElementCantContain,
+        kSaveLayerNeedsBounds,
+        kTargetIDNotFound,
+        kUnexpectedType
+    };
+    virtual ~SkDisplayXMLParserError();
+    virtual void getErrorString(SkString* str) const;
+    void setCode(ErrorCode code) { INHERITED::setCode((INHERITED::ErrorCode) code); }
+    void setInnerError(SkAnimateMaker* maker, const SkString& str);
+    typedef SkXMLParserError INHERITED;
+    friend class SkDisplayXMLParser;
+};
+
+class SkDisplayXMLParser : public SkXMLParser {
+public:
+    SkDisplayXMLParser(SkAnimateMaker& maker);
+    virtual ~SkDisplayXMLParser();
+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);
+private:
+    struct Parent {
+        SkDisplayable* fDisplayable;
+        SkDisplayTypes fType;
+    };
+    SkTDArray<Parent> fParents;
+    SkDisplayXMLParser& operator= (const SkDisplayXMLParser& );
+    SkDisplayXMLParserError* getError() { return (SkDisplayXMLParserError*) fError; }
+    const SkMemberInfo* searchContainer(const SkMemberInfo* ,
+        int infoCount);
+    SkAnimateMaker& fMaker;
+    SkBool fInInclude;
+    SkBool fInSkia;
+    // local state between onStartElement and onAddAttribute
+    SkDisplayable*  fCurrDisplayable;
+    SkDisplayTypes  fCurrType;
+    friend class SkXMLAnimatorWriter;
+    typedef SkXMLParser INHERITED;
+};
+
+#endif // SkDisplayXMLParser_DEFINED
+
+
diff --git a/legacy/src/animator/SkDisplayable.cpp b/legacy/src/animator/SkDisplayable.cpp
new file mode 100644
index 0000000..ebcc8a3
--- /dev/null
+++ b/legacy/src/animator/SkDisplayable.cpp
@@ -0,0 +1,558 @@
+
+/*
+ * 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 "SkDisplayable.h"
+#include "SkDisplayApply.h"
+#include "SkParse.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+#include "SkDisplayTypes.h"
+
+#ifdef SK_FIND_LEAKS
+// int SkDisplayable::fAllocationCount;
+SkTDDisplayableArray SkDisplayable::fAllocations;
+#endif
+
+#ifdef SK_DEBUG
+SkDisplayable::SkDisplayable() { 
+    id = _id.c_str();
+#ifdef SK_FIND_LEAKS
+    // fAllocationCount++;
+    *fAllocations.append() = this;
+#endif
+}
+#endif
+
+SkDisplayable::~SkDisplayable() {
+#ifdef SK_FIND_LEAKS
+    //  fAllocationCount--;
+    int index = fAllocations.find(this);
+    SkASSERT(index >= 0);
+    fAllocations.remove(index);
+#endif
+}
+
+bool SkDisplayable::add(SkAnimateMaker& , SkDisplayable* child) {
+    return false; 
+}
+
+//void SkDisplayable::apply(SkAnimateMaker& , const SkMemberInfo* , 
+//      SkDisplayable* , SkScalar [], int count) { 
+//  SkASSERT(0); 
+//}
+
+bool SkDisplayable::canContainDependents() const {
+    return false; 
+}
+ 
+bool SkDisplayable::childrenNeedDisposing() const { 
+    return false; 
+}
+
+void SkDisplayable::clearBounder() {
+}
+
+bool SkDisplayable::contains(SkDisplayable* ) {
+    return false;
+}
+
+SkDisplayable* SkDisplayable::contains(const SkString& ) {
+    return NULL;
+}
+
+SkDisplayable* SkDisplayable::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    if (type == SkType_Unknown) {
+        SkASSERT(0);
+        return NULL;
+    }
+    SkDisplayable* copy = SkDisplayType::CreateInstance(maker, type);
+    int index = -1;
+    int propIndex = 0;
+    const SkMemberInfo* info;
+    do {
+        info = copy->getMember(++index);
+        if (info == NULL)
+            break;
+        if (info->fType == SkType_MemberProperty) {
+            SkScriptValue value;
+            if (getProperty(propIndex, &value))
+                copy->setProperty(propIndex, value);
+            propIndex++;
+            continue;
+        }
+        if (info->fType == SkType_MemberFunction)
+            continue;
+        if (info->fType == SkType_Array) {
+            SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this);
+            int arrayCount;
+            if (array == NULL || (arrayCount = array->count()) == 0)
+                continue;
+            SkTDOperandArray* copyArray = (SkTDOperandArray*) info->memberData(copy);
+            copyArray->setCount(arrayCount);
+            SkDisplayTypes elementType;
+            if (type == SkType_Array) {
+                SkDisplayArray* dispArray = (SkDisplayArray*) this;
+                elementType = dispArray->values.getType();
+            } else
+                elementType = info->arrayType();
+            size_t elementSize = SkMemberInfo::GetSize(elementType);
+            size_t byteSize = elementSize * arrayCount;
+            memcpy(copyArray->begin(), array->begin(), byteSize);
+            continue;
+        }
+        if (SkDisplayType::IsDisplayable(maker, info->fType)) {
+            SkDisplayable** displayable = (SkDisplayable**) info->memberData(this);
+            if (*displayable == NULL || *displayable == (SkDisplayable*) -1)
+                continue;
+            SkDisplayable* deeper = (*displayable)->deepCopy(maker);
+            info->setMemberData(copy, deeper, sizeof(deeper));
+            continue;
+        }
+        if (info->fType == SkType_String || info->fType == SkType_DynamicString) {
+            SkString* string;
+            info->getString(this, &string);
+            info->setString(copy, string);
+            continue;
+        }
+        void* data = info->memberData(this);
+        size_t size = SkMemberInfo::GetSize(info->fType);
+        info->setMemberData(copy, data, size);
+    } while (true);
+    copy->dirty();
+    return copy;
+}
+
+void SkDisplayable::dirty() {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayable::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+#if SK_USE_CONDENSED_INFO == 0
+    this->dumpAttrs(maker);
+    this->dumpChildren(maker);
+#endif
+}
+
+void SkDisplayable::dumpAttrs(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    if (type == SkType_Unknown) {
+        //SkDebugf("/>\n");
+        return;
+    }
+    SkDisplayable* blankCopy = SkDisplayType::CreateInstance(maker, type);
+
+    int index = -1;
+    int propIndex = 0;
+    const SkMemberInfo* info;
+    const SkMemberInfo* blankInfo;
+    SkScriptValue value;
+    SkScriptValue blankValue;
+    SkOperand values[2];
+    SkOperand blankValues[2];
+    do {
+        info = this->getMember(++index);
+        if (NULL == info) {
+            //SkDebugf("\n");
+            break;
+        }
+        if (SkType_MemberProperty == info->fType) {
+            if (getProperty(propIndex, &value)) {
+                blankCopy->getProperty(propIndex, &blankValue);
+                //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;
+            if (array == NULL || (arrayCount = array->count()) == 0)
+                continue;
+            SkDisplayTypes elementType;
+            if (type == SkType_Array) {
+                SkDisplayArray* dispArray = (SkDisplayArray*) this;
+                elementType = dispArray->values.getType();
+            } else
+                elementType = info->arrayType();
+            bool firstElem = true;
+            SkDebugf("%s=\"[", info->fName);
+            for (SkOperand* op = array->begin(); op < array->end(); op++) {
+                if (!firstElem) SkDebugf(",");
+                switch (elementType) {
+                        case SkType_Displayable:
+                            SkDebugf("%s", op->fDisplayable->id);
+                            break;
+                        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:    
+                            SkDebugf("%s", op->fString->c_str());
+                            break;
+                        default:
+                            break;
+                }
+                firstElem = false;
+            }
+            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()); 
+            continue;
+        }
+        
+        
+        blankInfo = blankCopy->getMember(index);
+        int i = info->fCount;
+        info->getValue(this, values, i);
+        blankInfo->getValue(blankCopy, blankValues, i);
+        dumpValues(info, info->fType, values[0], blankValues[0], values[1], blankValues[1]);
+    } while (true);
+    delete blankCopy;
+}
+
+void SkDisplayable::dumpBase(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    const char* elementName = "(unknown)";
+    if (type != SkType_Unknown && type != SkType_Screenplay)
+        elementName = SkDisplayType::GetName(maker, type);
+    SkDebugf("%*s", SkDisplayList::fIndent, "");
+    if (SkDisplayList::fDumpIndex != 0 && SkDisplayList::fIndent == 0)
+        SkDebugf("%d: ", SkDisplayList::fDumpIndex);
+    SkDebugf("<%s ", elementName);
+    if (strcmp(id,"") != 0)
+        SkDebugf("id=\"%s\" ", id);
+}
+
+void SkDisplayable::dumpChildren(SkAnimateMaker* maker, bool closedAngle) {
+    
+    int index = -1;
+    const SkMemberInfo* info;
+    index = -1;
+    SkDisplayList::fIndent += 4;
+    do {
+        info = this->getMember(++index);
+        if (NULL == info) {
+            break;
+        }
+        if (SkDisplayType::IsDisplayable(maker, info->fType)) {
+            SkDisplayable** displayable = (SkDisplayable**) info->memberData(this);
+            if (*displayable == NULL || *displayable == (SkDisplayable*) -1)
+                continue;
+            if (closedAngle == false) {
+                SkDebugf(">\n");
+                closedAngle = true;
+            }
+            (*displayable)->dump(maker);
+        }
+    } while (true);
+    SkDisplayList::fIndent -= 4;
+    if (closedAngle)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+}
+
+void SkDisplayable::dumpEnd(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    const char* elementName = "(unknown)";
+    if (type != SkType_Unknown && type != SkType_Screenplay)
+        elementName = SkDisplayType::GetName(maker, type);
+    SkDebugf("%*s", SkDisplayList::fIndent, "");
+    SkDebugf("</%s>\n", elementName);
+}
+
+void SkDisplayable::dumpEvents() {
+}
+
+void SkDisplayable::dumpValues(const SkMemberInfo* info, SkDisplayTypes type, SkOperand op, SkOperand blankOp,
+    SkOperand op2, SkOperand blankOp2) {
+    switch (type) {
+    case SkType_BitmapEncoding:
+        switch (op.fS32) {
+            case 0 : SkDebugf("type=\"jpeg\" ");
+                break;
+            case 1 : SkDebugf("type=\"png\" ");
+                break;
+            default: SkDebugf("type=\"UNDEFINED\" ");
+        }
+        break;
+    //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:
+        switch (op.fS32) {
+            case 0:
+                //don't want to print anything for 0, just adding it to remove it from default:
+                break;
+            case 1:
+                SkDebugf("%s=\"%s\" ", info->fName, "angle");
+                break;
+            case 2:
+                SkDebugf("%s=\"%s\" ", info->fName, "position");
+                break;
+            default:
+                SkDebugf("%s=\"INVALID\" ", info->fName);
+        }
+        break;
+    case SkType_MaskFilterBlurStyle:
+        switch (op.fS32) {
+            case 0:
+                break;
+            case 1:
+                SkDebugf("%s=\"%s\" ", info->fName, "solid");
+                break;
+            case 2:
+                SkDebugf("%s=\"%s\" ", info->fName, "outer");
+                break;
+            case 3:
+                SkDebugf("%s=\"%s\" ", info->fName, "inner");
+                break;
+            default:
+                SkDebugf("%s=\"INVALID\" ", info->fName);
+        }
+        break;
+    case SkType_FilterType:
+        if (op.fS32 == 1)
+            SkDebugf("%s=\"%s\" ", info->fName, "bilinear");
+        break;
+    case SkType_PathDirection:
+        SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "cw" : "ccw");
+        break;
+    case SkType_FillType:
+        SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "winding" : "evenOdd");
+        break;
+    case SkType_TileMode:
+        //correct to look at the S32?
+        if (op.fS32 != blankOp.fS32) 
+            SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "clamp" : op.fS32 == 1 ? "repeat" : "mirror");
+        break;
+    case SkType_Boolean:
+        if (op.fS32 != blankOp.fS32)
+            SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "false" : "true");
+        break;
+    case SkType_Int:
+        if (op.fS32 != blankOp.fS32)
+            SkDebugf(" %s=\"%d\"  ", info->fName, op.fS32);
+        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) 
+            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& ) { 
+    return false;
+}
+
+void SkDisplayable::enableBounder() {
+}
+
+void SkDisplayable::executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& , SkDisplayTypes, SkScriptValue*  ) {
+    SkASSERT(0); 
+}
+
+void SkDisplayable::executeFunction(SkDisplayable* target, 
+        const SkMemberInfo* info, SkTypedArray* values, SkScriptValue* value) {
+    SkTDArray<SkScriptValue> typedValues;
+    for (SkOperand* op = values->begin(); op < values->end(); op++) {
+        SkScriptValue temp;
+        temp.fType = values->getType();
+        temp.fOperand = *op;
+        *typedValues.append() = temp;
+    }
+    executeFunction(target, info->functionIndex(), typedValues, info->getType(), value);
+}
+
+void SkDisplayable::executeFunction2(SkDisplayable* , int index, 
+        SkOpArray* params, SkDisplayTypes, SkOperand2*  ) {
+    SkASSERT(0); 
+}
+
+void SkDisplayable::getBounds(SkRect* rect) {
+    SkASSERT(rect);
+    rect->fLeft = rect->fTop = SK_ScalarMax;
+    rect->fRight= rect->fBottom = -SK_ScalarMax;
+}
+
+const SkFunctionParamType* SkDisplayable::getFunctionsParameters() {
+    return NULL;
+}
+
+const SkMemberInfo* SkDisplayable::getMember(int index) { 
+    return NULL; 
+}
+
+const SkMemberInfo* SkDisplayable::getMember(const char name[]) { 
+    return NULL; 
+}
+
+const SkFunctionParamType* SkDisplayable::getParameters(const SkMemberInfo* info, 
+        int* paramCount) {
+    const SkFunctionParamType* params = getFunctionsParameters();
+    SkASSERT(params != NULL);
+    int funcIndex = info->functionIndex();
+    // !!! eventually break traversing params into an external function (maybe this whole function)
+    int index = funcIndex;
+    int offset = 0;
+    while (--index >= 0) {
+        while (params[offset] != 0)
+            offset++;
+        offset++;
+    }
+    int count = 0;
+    while (params[offset] != 0) {
+        count++;
+        offset++;
+    }
+    *paramCount = count;
+    return &params[offset - count];
+}
+
+SkDisplayable* SkDisplayable::getParent() const {
+    return NULL;
+}
+
+bool SkDisplayable::getProperty(int index, SkScriptValue* ) const {
+//  SkASSERT(0); 
+    return false; 
+}
+
+bool SkDisplayable::getProperty2(int index, SkOperand2* value) const {
+    SkASSERT(0); 
+    return false; 
+}
+
+SkDisplayTypes SkDisplayable::getType() const { 
+    return SkType_Unknown; 
+}
+
+bool SkDisplayable::hasEnable() const {
+    return false;
+}
+
+bool SkDisplayable::isDrawable() const {
+    return false; 
+}
+
+void SkDisplayable::onEndElement(SkAnimateMaker& ) {}
+
+const SkMemberInfo* SkDisplayable::preferredChild(SkDisplayTypes type) {
+    return NULL;
+}
+
+bool SkDisplayable::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) {
+    return false;
+}
+
+//SkDisplayable* SkDisplayable::resolveTarget(SkAnimateMaker& ) { 
+//  return this; 
+//}
+
+void SkDisplayable::setChildHasID() {
+}
+
+bool SkDisplayable::setParent(SkDisplayable* ) {
+    return false;
+}
+
+bool SkDisplayable::setProperty(int index, SkScriptValue& ) {
+    //SkASSERT(0); 
+    return false; 
+}
+
+void SkDisplayable::setReference(const SkMemberInfo* info, SkDisplayable* displayable) {
+    if (info->fType == SkType_MemberProperty) {
+        SkScriptValue scriptValue;
+        scriptValue.fOperand.fDisplayable = displayable;
+        scriptValue.fType = displayable->getType();
+        setProperty(info->propertyIndex(), scriptValue);
+    } else if (info->fType == SkType_Array) {
+        SkASSERT(displayable->getType() == SkType_Array);
+        SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
+        SkTDScalarArray* array = (SkTDScalarArray* ) info->memberData(this);
+        array->setCount(dispArray->values.count());
+        memcpy(array->begin(), dispArray->values.begin(), dispArray->values.count() * sizeof(int));
+        //
+
+        // !!! need a way for interpreter engine to own array
+        // !!! probably need to replace all scriptable arrays with single bigger array
+        // that has operand and type on every element -- or
+        // when array is dirtied, need to get parent to reparse to local array
+    } else {
+        void* storage = info->memberData(this);
+        memcpy(storage, &displayable, sizeof(SkDisplayable*));
+    }
+// !!! unclear why displayable is dirtied here
+// if this is called, this breaks fromPath.xml
+//  displayable->dirty();
+}
+
+#ifdef SK_DEBUG
+void SkDisplayable::validate() {
+}
+#endif
+
+
diff --git a/legacy/src/animator/SkDisplayable.h b/legacy/src/animator/SkDisplayable.h
new file mode 100644
index 0000000..a3db002
--- /dev/null
+++ b/legacy/src/animator/SkDisplayable.h
@@ -0,0 +1,112 @@
+
+/*
+ * 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 SkDisplayable_DEFINED
+#define SkDisplayable_DEFINED
+
+#include "SkOperand.h"
+#ifdef SK_DEBUG
+#include "SkString.h"
+#endif
+#include "SkIntArray.h"
+#include "SkRect.h"
+#include "SkTDArray.h"
+
+class SkAnimateMaker;
+class SkApply;
+class SkEvents;
+struct SkMemberInfo;
+struct SkScriptValue;
+class SkOpArray; // compiled scripting experiment
+union SkOperand2; // compiled scripting experiment
+
+class SkDisplayable {
+public:
+#ifdef SK_DEBUG
+    SkDisplayable();
+#endif
+    virtual ~SkDisplayable();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool canContainDependents() const;
+    virtual bool childrenNeedDisposing() const;
+    virtual void clearBounder();
+    virtual bool contains(SkDisplayable* );
+    virtual SkDisplayable* contains(const SkString& );
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual void dirty();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+    void dumpAttrs(SkAnimateMaker* );
+    void dumpBase(SkAnimateMaker* );
+    void dumpChildren(SkAnimateMaker* maker, bool closedAngle = false );
+    void dumpEnd(SkAnimateMaker* );
+    virtual void dumpEvents();
+#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, 
+        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, 
+        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(); 
+        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 || 
+        getType() == SkType_Save || getType() == SkType_DrawTo ||
+        getType() == SkType_SaveLayer; }
+    bool isMatrix() const { return getType() == SkType_Matrix; }
+    virtual bool isPaint() const { return getType() == SkType_Paint; }
+    virtual bool isPath() const { return false; }
+    bool isPost() const { return getType() == SkType_Post; }
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+    virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* );
+    virtual void setChildHasID();
+    virtual bool setParent(SkDisplayable* );
+    virtual bool setProperty(int index, SkScriptValue& );
+    void setReference(const SkMemberInfo* info, SkDisplayable* ref);
+#ifdef SK_DEBUG
+    bool isDataInput() const { return getType() == SkType_DataInput; };
+    bool isEvent() const { return getType() == SkType_Event; }
+    virtual bool isMatrixPart() const { return false; }
+    bool isPatch() const { return getType() == SkType_3D_Patch; }
+    virtual bool isPaintPart() const { return false; }
+    virtual bool isPathPart() const { return false; }
+    virtual void validate();
+    SkString _id;
+    const char* id;
+//  static int fAllocationCount;
+    static SkTDDisplayableArray fAllocations;
+#else
+    void validate() {}
+#endif
+#ifdef SK_DUMP_ENABLED
+private:
+    void dumpValues(const SkMemberInfo* info, SkDisplayTypes type, SkOperand op, SkOperand blankOp,
+        SkOperand op2, SkOperand blankOp2);
+#endif
+};
+
+#endif // SkDisplayable_DEFINED
diff --git a/legacy/src/animator/SkDraw3D.cpp b/legacy/src/animator/SkDraw3D.cpp
new file mode 100644
index 0000000..ce2bec4
--- /dev/null
+++ b/legacy/src/animator/SkDraw3D.cpp
@@ -0,0 +1,109 @@
+
+/*
+ * 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 "SkDraw3D.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkTypedArray.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk3D_Point::fInfo[] = {
+    SK_MEMBER_ALIAS(x, fPoint.fX, Float),
+    SK_MEMBER_ALIAS(y, fPoint.fY, Float),
+    SK_MEMBER_ALIAS(z, fPoint.fZ, Float)
+};
+
+#endif
+
+DEFINE_NO_VIRTUALS_GET_MEMBER(Sk3D_Point);
+
+Sk3D_Point::Sk3D_Point() {
+    fPoint.set(0, 0, 0);    
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk3D_Camera::fInfo[] = {
+    SK_MEMBER_ALIAS(axis, fCamera.fAxis, 3D_Point),
+    SK_MEMBER(hackHeight, Float),
+    SK_MEMBER(hackWidth, Float),
+    SK_MEMBER_ALIAS(location, fCamera.fLocation, 3D_Point),
+    SK_MEMBER_ALIAS(observer, fCamera.fObserver, 3D_Point),
+    SK_MEMBER(patch, 3D_Patch),
+    SK_MEMBER_ALIAS(zenith, fCamera.fZenith, 3D_Point),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(Sk3D_Camera);
+
+Sk3D_Camera::Sk3D_Camera() : hackWidth(0), hackHeight(0), patch(NULL) {
+}
+
+Sk3D_Camera::~Sk3D_Camera() {
+}
+
+bool Sk3D_Camera::draw(SkAnimateMaker& maker) {
+    fCamera.update();
+    SkMatrix matrix;
+    fCamera.patchToMatrix(patch->fPatch, &matrix);
+    matrix.preTranslate(hackWidth / 2, -hackHeight / 2);
+    matrix.postTranslate(hackWidth / 2, hackHeight / 2);
+    maker.fCanvas->concat(matrix);
+    return false;
+}
+
+
+enum Sk3D_Patch_Functions {
+    SK_FUNCTION(rotateDegrees)
+};
+
+const SkFunctionParamType Sk3D_Patch::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) 0 // terminator for parameter list (there may be multiple parameter lists)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk3D_Patch::fInfo[] = {
+    SK_MEMBER_ALIAS(origin, fPatch.fOrigin, 3D_Point),
+    SK_MEMBER_FUNCTION(rotateDegrees, Float),
+    SK_MEMBER_ALIAS(u, fPatch.fU, 3D_Point),
+    SK_MEMBER_ALIAS(v, fPatch.fV, 3D_Point)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(Sk3D_Patch);
+
+void Sk3D_Patch::executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* ) {
+    SkASSERT(target == this);
+    switch (index) {
+        case SK_FUNCTION(rotateDegrees):
+            SkASSERT(parameters.count() == 3);
+            SkASSERT(type == SkType_Float);
+            fPatch.rotateDegrees(parameters[0].fOperand.fScalar, 
+                parameters[1].fOperand.fScalar, parameters[2].fOperand.fScalar);
+            break;
+        default:
+            SkASSERT(0);
+    }
+}
+
+const SkFunctionParamType* Sk3D_Patch::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+
+
diff --git a/legacy/src/animator/SkDraw3D.h b/legacy/src/animator/SkDraw3D.h
new file mode 100644
index 0000000..e6549ea
--- /dev/null
+++ b/legacy/src/animator/SkDraw3D.h
@@ -0,0 +1,51 @@
+
+/*
+ * 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 SkDraw3D_DEFINED
+#define SkDraw3D_DEFINED
+
+#include "SkCamera.h"
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+
+class Sk3D_Patch;
+
+struct Sk3D_Point {
+    DECLARE_NO_VIRTUALS_MEMBER_INFO(3D_Point);
+    Sk3D_Point();
+private:
+    SkPoint3D fPoint;
+};
+
+class Sk3D_Camera : public SkDrawable {
+    DECLARE_MEMBER_INFO(3D_Camera);
+    Sk3D_Camera();
+    virtual ~Sk3D_Camera();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkScalar hackWidth;
+    SkScalar hackHeight;
+    SkCamera3D fCamera;
+    Sk3D_Patch* patch;
+};
+
+class Sk3D_Patch : public SkDisplayable {
+    DECLARE_MEMBER_INFO(3D_Patch);
+private:
+    virtual void executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    SkPatch3D  fPatch;
+    static const SkFunctionParamType fFunctionParameters[];
+    friend class Sk3D_Camera;
+};
+
+#endif // SkDraw3D_DEFINED
+
diff --git a/legacy/src/animator/SkDrawBitmap.cpp b/legacy/src/animator/SkDrawBitmap.cpp
new file mode 100644
index 0000000..a1d49d6
--- /dev/null
+++ b/legacy/src/animator/SkDrawBitmap.cpp
@@ -0,0 +1,198 @@
+
+/*
+ * 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 "SkDrawBitmap.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkBaseBitmap::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkBaseBitmap);
+
+SkBaseBitmap::SkBaseBitmap() : x(0), y(0) {
+}
+
+SkBaseBitmap::~SkBaseBitmap() {
+}
+
+bool SkBaseBitmap::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawBitmap(fBitmap, x, y, maker.fPaint);
+    return false;
+}
+
+enum SkDrawBitmap_Properties {
+    SK_PROPERTY(erase)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawBitmap::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_PROPERTY(erase, ARGB),
+    SK_MEMBER(format, BitmapFormat),
+    SK_MEMBER(height, Int),
+    SK_MEMBER(rowBytes, Int),
+    SK_MEMBER(width, Int),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawBitmap);
+
+SkDrawBitmap::SkDrawBitmap() : format((SkBitmap::Config) -1), height(-1), 
+    rowBytes(0),    width(-1), fColor(0), fColorSet(false) {
+}
+
+SkDrawBitmap::~SkDrawBitmap() {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawBitmap::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (fColorSet)
+        SkDebugf("erase=\"argb(%d,%d,%d,%d)\" ", SkColorGetA(fColor)/255, SkColorGetR(fColor),
+            SkColorGetG(fColor), SkColorGetB(fColor));
+    if (rowBytes > 0)
+        SkDebugf("rowBytes=\"%d\" ", rowBytes);
+    const char* formatName;
+    switch (format) {
+        case 0: formatName = "none"; break;
+        case 1: formatName = "A1"; break;
+        case 2: formatName = "A8"; break;
+        case 3: formatName = "Index8"; break;
+        case 4: formatName = "RGB16"; break;
+        case 5: formatName = "RGB32"; break;
+    }
+    SkDebugf("format=\"%s\" />\n", formatName);
+}
+#endif
+
+void SkDrawBitmap::onEndElement(SkAnimateMaker& maker) {
+    SkASSERT(format != (SkBitmap::Config) -1);
+    SkASSERT(width != -1);
+    SkASSERT(height != -1);
+    SkASSERT(rowBytes >= 0);
+    fBitmap.setConfig((SkBitmap::Config) format, width, height, rowBytes);
+    fBitmap.allocPixels();
+    if (fColorSet)
+        fBitmap.eraseColor(fColor);
+}
+
+bool SkDrawBitmap::setProperty(int index, SkScriptValue& value)
+{
+    switch (index) {
+        case SK_PROPERTY(erase):
+            SkASSERT(value.fType == SkType_ARGB);
+            fColor = value.fOperand.fS32;
+            fColorSet = true;
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+
+enum SkImage_Properties {
+    SK_PROPERTY(height),
+    SK_PROPERTY(width)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkImage::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(base64, Base64),
+    SK_MEMBER_PROPERTY(height, Int),
+    SK_MEMBER(src, String),
+    SK_MEMBER_PROPERTY(width, Int)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkImage);
+
+SkImage::SkImage() : fDirty(true), fUriBase(NULL) {
+    base64.fData = NULL;
+    base64.fLength = 0;
+}
+
+SkImage::~SkImage() {
+    delete[] base64.fData; 
+}
+
+SkDisplayable* SkImage::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayable* copy = INHERITED::deepCopy(maker);
+    ((SkImage*) copy)->fUriBase = ((SkImage*) this)->fUriBase;
+    return copy;
+}
+
+void SkImage::dirty() {
+    fDirty = true;
+}
+
+bool SkImage::draw(SkAnimateMaker& maker) {
+    if (fDirty) 
+        resolve();
+    return INHERITED::draw(maker);
+}
+
+bool SkImage::getProperty(int index, SkScriptValue* value) const {
+    if (fDirty) 
+        resolve();
+    switch (index) {
+        case SK_PROPERTY(height):
+            value->fOperand.fS32 = fBitmap.height();
+            break;
+        case SK_PROPERTY(width):
+            value->fOperand.fS32 = fBitmap.width();
+            break;
+    default:
+        SkASSERT(0);
+        return false;
+    }
+    value->fType = SkType_Int;
+    return true;
+}
+
+void SkImage::onEndElement(SkAnimateMaker& maker) {
+    fUriBase = maker.fPrefix.c_str();
+}
+
+void SkImage::resolve() {
+    fDirty = false;
+    if (base64.fData) {
+        fBitmap.reset();
+        SkImageDecoder::DecodeMemory(base64.fData, base64.fLength, &fBitmap);
+    } else if (src.size()) {
+        if (fLast.equals(src))
+            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/legacy/src/animator/SkDrawBitmap.h b/legacy/src/animator/SkDrawBitmap.h
new file mode 100644
index 0000000..e6d6d44
--- /dev/null
+++ b/legacy/src/animator/SkDrawBitmap.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 SkDrawBitmap_DEFINED
+#define SkDrawBitmap_DEFINED
+
+#include "SkBoundable.h"
+#include "SkBase64.h"
+#include "SkBitmap.h"
+// #include "SkImageDecoder.h"
+#include "SkMemberInfo.h"
+
+class SkBaseBitmap : public SkBoundable {
+    DECLARE_MEMBER_INFO(BaseBitmap);
+    SkBaseBitmap();
+    virtual ~SkBaseBitmap();
+    virtual bool draw(SkAnimateMaker& );
+protected:
+    SkBitmap fBitmap;
+    SkScalar x;
+    SkScalar y;
+private:
+    friend class SkDrawTo;
+    friend class SkDrawBitmapShader;
+    typedef SkBoundable INHERITED;
+};
+
+class SkDrawBitmap : public SkBaseBitmap {
+    DECLARE_DRAW_MEMBER_INFO(Bitmap);
+    SkDrawBitmap();
+    virtual ~SkDrawBitmap();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual bool setProperty(int index, SkScriptValue& value);
+protected:
+    int /*SkBitmap::Config*/ format;
+    int32_t height;
+    int32_t rowBytes;
+    int32_t width;
+    SkColor fColor;
+    SkBool fColorSet;
+    typedef SkBaseBitmap INHERITED;
+};
+
+class SkImage : public SkBaseBitmap {
+    DECLARE_MEMBER_INFO(Image);
+    SkImage();
+    virtual ~SkImage();
+    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();
+protected:
+    SkBase64 base64;
+    SkString src;
+    SkString fLast; // cache of src so that stream isn't unnecessarily decoded 
+    SkBool fDirty;
+    const char* fUriBase;
+    typedef SkBaseBitmap INHERITED;
+};
+
+#endif // SkDrawBitmap_DEFINED
diff --git a/legacy/src/animator/SkDrawBlur.cpp b/legacy/src/animator/SkDrawBlur.cpp
new file mode 100644
index 0000000..57e8e20
--- /dev/null
+++ b/legacy/src/animator/SkDrawBlur.cpp
@@ -0,0 +1,32 @@
+
+/*
+ * 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 "SkDrawBlur.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawBlur::fInfo[] = {
+    SK_MEMBER(blurStyle, MaskFilterBlurStyle),
+    SK_MEMBER(radius, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawBlur);
+
+SkDrawBlur::SkDrawBlur() : radius(-1), 
+    blurStyle(SkBlurMaskFilter::kNormal_BlurStyle) {
+}
+
+SkMaskFilter* SkDrawBlur::getMaskFilter() {
+    if (radius < 0)
+        return NULL;
+    return SkBlurMaskFilter::Create(radius, (SkBlurMaskFilter::BlurStyle) blurStyle);
+}
+
diff --git a/legacy/src/animator/SkDrawBlur.h b/legacy/src/animator/SkDrawBlur.h
new file mode 100644
index 0000000..220e211
--- /dev/null
+++ b/legacy/src/animator/SkDrawBlur.h
@@ -0,0 +1,26 @@
+
+/*
+ * 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 SkDrawBlur_DEFINED
+#define SkDrawBlur_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkBlurMaskFilter.h"
+
+class SkDrawBlur : public SkDrawMaskFilter {
+    DECLARE_DRAW_MEMBER_INFO(Blur);
+    SkDrawBlur();
+    virtual SkMaskFilter* getMaskFilter();
+protected:
+    SkScalar radius;
+    int /*SkBlurMaskFilter::BlurStyle*/ blurStyle;
+};
+
+#endif // SkDrawBlur_DEFINED
+
diff --git a/legacy/src/animator/SkDrawClip.cpp b/legacy/src/animator/SkDrawClip.cpp
new file mode 100644
index 0000000..521bbc5
--- /dev/null
+++ b/legacy/src/animator/SkDrawClip.cpp
@@ -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.
+ */
+
+
+#include "SkDrawClip.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawRectangle.h"
+#include "SkDrawPath.h"
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawClip::fInfo[] = {
+    SK_MEMBER(path, Path),
+    SK_MEMBER(rect, Rect)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawClip);
+
+SkDrawClip::SkDrawClip() : rect(NULL), path(NULL) {
+}
+
+bool SkDrawClip::draw(SkAnimateMaker& maker ) {
+    if (rect != NULL)
+        maker.fCanvas->clipRect(rect->fRect);
+    else {
+        SkASSERT(path != NULL);
+        maker.fCanvas->clipPath(path->fPath);
+    }
+    return false;
+}
+
diff --git a/legacy/src/animator/SkDrawClip.h b/legacy/src/animator/SkDrawClip.h
new file mode 100644
index 0000000..6265775
--- /dev/null
+++ b/legacy/src/animator/SkDrawClip.h
@@ -0,0 +1,29 @@
+
+/*
+ * 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 SkDrawClip_DEFINED
+#define SkDrawClip_DEFINED
+
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+#include "SkRegion.h"
+
+class SkDrawPath;
+class SkDrawRect;
+
+class SkDrawClip : public SkDrawable {
+    DECLARE_DRAW_MEMBER_INFO(Clip);
+    SkDrawClip();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkDrawRect* rect;
+    SkDrawPath* path;
+};
+
+#endif // SkDrawClip_DEFINED
diff --git a/legacy/src/animator/SkDrawColor.cpp b/legacy/src/animator/SkDrawColor.cpp
new file mode 100644
index 0000000..811a5d6
--- /dev/null
+++ b/legacy/src/animator/SkDrawColor.cpp
@@ -0,0 +1,270 @@
+
+/*
+ * 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 "SkDrawColor.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+#include "SkDrawPaint.h"
+#include "SkParse.h"
+#include "SkScript.h"
+
+enum HSV_Choice {
+    kGetHue,
+    kGetSaturation,
+    kGetValue
+};
+
+static SkScalar RGB_to_HSV(SkColor color, HSV_Choice choice) {
+    SkScalar red = SkIntToScalar(SkColorGetR(color));
+    SkScalar green = SkIntToScalar(SkColorGetG(color));
+    SkScalar blue = SkIntToScalar(SkColorGetB(color));
+    SkScalar min = SkMinScalar(SkMinScalar(red, green), blue);
+    SkScalar value = SkMaxScalar(SkMaxScalar(red, green), blue);
+    if (choice == kGetValue)
+        return value/255;
+    SkScalar delta = value - min;
+    SkScalar saturation = value == 0 ? 0 : SkScalarDiv(delta, value);
+    if (choice == kGetSaturation)
+        return saturation;
+    SkScalar hue;
+    if (saturation == 0)
+        hue = 0;
+    else {
+        SkScalar part60 = SkScalarDiv(60 * SK_Scalar1, delta);
+        if (red == value) {
+            hue = SkScalarMul(green - blue, part60);
+            if (hue < 0)
+                hue += 360 * SK_Scalar1;
+        }
+        else if (green == value)
+            hue = 120 * SK_Scalar1 + SkScalarMul(blue - red, part60);
+        else  // blue == value
+            hue = 240 * SK_Scalar1 + SkScalarMul(red - green, part60);
+    }
+    SkASSERT(choice == kGetHue);
+    return hue;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable 'red', etc. may be used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+static SkColor HSV_to_RGB(SkColor color, HSV_Choice choice, SkScalar hsv) {
+    SkScalar hue = choice == kGetHue ? hsv : RGB_to_HSV(color, kGetHue);
+    SkScalar saturation = choice == kGetSaturation ? hsv : RGB_to_HSV(color, kGetSaturation);
+    SkScalar value = choice == kGetValue ? hsv : RGB_to_HSV(color, kGetValue);
+    value *= 255;
+    SkScalar red SK_INIT_TO_AVOID_WARNING;
+    SkScalar green SK_INIT_TO_AVOID_WARNING;
+    SkScalar blue SK_INIT_TO_AVOID_WARNING;
+    if (saturation == 0)    // color is on black-and-white center line
+        red = green = blue = value;
+    else {
+        //SkScalar fraction = SkScalarMod(hue, 60 * SK_Scalar1);
+        int sextant = SkScalarFloor(hue / 60);
+        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 - 
+            SkScalarMul(saturation, SK_Scalar1 - fraction));
+        switch (sextant % 6) {
+            case 0: red = value; green = t; blue = p; break;
+            case 1: red = q; green = value; blue = p; break;
+            case 2: red = p; green = value; blue = t; break;
+            case 3: red = p; green = q; blue = value; break;
+            case 4: red = t;  green = p; blue = value; break;
+            case 5: red = value; green = p; blue = q; break;
+        }
+    }
+    //used to say SkToU8((U8CPU) red) etc
+    return SkColorSetARGB(SkColorGetA(color), SkScalarRound(red), 
+        SkScalarRound(green), SkScalarRound(blue));
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  
+#pragma warning ( pop )
+#endif
+
+enum SkDrawColor_Properties {
+    SK_PROPERTY(alpha),
+    SK_PROPERTY(blue),
+    SK_PROPERTY(green),
+    SK_PROPERTY(hue),
+    SK_PROPERTY(red),
+    SK_PROPERTY(saturation),
+    SK_PROPERTY(value)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawColor::fInfo[] = {
+    SK_MEMBER_PROPERTY(alpha, Float),
+    SK_MEMBER_PROPERTY(blue, Float),
+    SK_MEMBER(color, ARGB),
+    SK_MEMBER_PROPERTY(green, Float),
+    SK_MEMBER_PROPERTY(hue, Float),
+    SK_MEMBER_PROPERTY(red, Float),
+    SK_MEMBER_PROPERTY(saturation, Float),
+    SK_MEMBER_PROPERTY(value, Float),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawColor);
+
+SkDrawColor::SkDrawColor() : fDirty(false) { 
+    color = SK_ColorBLACK; 
+    fHue = fSaturation = fValue = SK_ScalarNaN;
+}
+
+bool SkDrawColor::add() {
+    if (fPaint->color != NULL)
+        return true; // error (probably color in paint as attribute as well)
+    fPaint->color = this;
+    fPaint->fOwnsColor = true;
+    return false;
+}
+
+SkDisplayable* SkDrawColor::deepCopy(SkAnimateMaker* maker) {
+    SkDrawColor* copy = new SkDrawColor();
+    copy->color = color;
+    copy->fHue = fHue;
+    copy->fSaturation = fSaturation;
+    copy->fValue = fValue;
+    copy->fDirty = fDirty;
+    return copy;
+}
+
+void SkDrawColor::dirty(){
+    fDirty = true;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawColor::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("alpha=\"%d\" red=\"%d\" green=\"%d\" blue=\"%d\" />\n",  
+        SkColorGetA(color)/255, SkColorGetR(color),
+        SkColorGetG(color), SkColorGetB(color));
+}
+#endif
+
+SkColor SkDrawColor::getColor() { 
+    if (fDirty) {
+        if (SkScalarIsNaN(fValue) == false)
+            color = HSV_to_RGB(color, kGetValue, fValue);
+        if (SkScalarIsNaN(fSaturation) == false)
+            color = HSV_to_RGB(color, kGetSaturation, fSaturation);
+        if (SkScalarIsNaN(fHue) == false)
+            color = HSV_to_RGB(color, kGetHue, fHue);
+        fDirty = false;
+    }
+    return color; 
+}
+
+SkDisplayable* SkDrawColor::getParent() const {
+    return fPaint;
+}
+
+bool SkDrawColor::getProperty(int index, SkScriptValue* value) const {
+    value->fType = SkType_Float;
+    SkScalar result;
+    switch(index) {
+        case SK_PROPERTY(alpha):
+            result = SkIntToScalar(SkColorGetA(color)) / 255;
+            break;
+        case SK_PROPERTY(blue):
+            result = SkIntToScalar(SkColorGetB(color));
+            break;
+        case SK_PROPERTY(green):
+            result = SkIntToScalar(SkColorGetG(color));
+            break;
+        case SK_PROPERTY(hue):
+            result = RGB_to_HSV(color, kGetHue);
+            break;
+        case SK_PROPERTY(red):
+            result = SkIntToScalar(SkColorGetR(color));
+            break;
+        case SK_PROPERTY(saturation):
+            result = RGB_to_HSV(color, kGetSaturation);
+            break;
+        case SK_PROPERTY(value):
+            result = RGB_to_HSV(color, kGetValue);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fOperand.fScalar = result;
+    return true;
+}
+
+void SkDrawColor::onEndElement(SkAnimateMaker& maker){
+    fDirty = true;
+}
+
+bool SkDrawColor::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->getType() == SkType_LinearGradient || parent->getType() == SkType_RadialGradient)
+        return false;
+    if (parent->isPaint() == false)
+        return true;
+    fPaint = (SkDrawPaint*) parent;
+    return false;
+}
+
+bool SkDrawColor::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(value.fType == SkType_Float);
+    SkScalar scalar = value.fOperand.fScalar;
+    switch (index) {
+        case SK_PROPERTY(alpha):
+            uint8_t alpha;
+        #ifdef SK_SCALAR_IS_FLOAT
+            alpha = scalar == SK_Scalar1 ? 255 : SkToU8((U8CPU) (scalar * 256));
+        #else
+            alpha = SkToU8((scalar - (scalar >= SK_ScalarHalf)) >> 8);
+        #endif
+            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), 
+                SkColorGetG(color), SkToU8((U8CPU) scalar));
+            break;
+        case SK_PROPERTY(green):
+            scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
+            color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color), 
+                SkToU8((U8CPU) scalar), SkColorGetB(color));
+            break;
+        case SK_PROPERTY(hue):
+            fHue = scalar;//RGB_to_HSV(color, kGetHue);
+            fDirty = true;
+            break;
+        case SK_PROPERTY(red):
+            scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
+            color = SkColorSetARGB(SkColorGetA(color), SkToU8((U8CPU) scalar), 
+                SkColorGetG(color), SkColorGetB(color));
+        break;
+        case SK_PROPERTY(saturation):
+            fSaturation = scalar;//RGB_to_HSV(color, kGetSaturation);
+            fDirty = true;
+            break;
+        case SK_PROPERTY(value):
+            fValue = scalar;//RGB_to_HSV(color, kGetValue);
+            fDirty = true;
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
diff --git a/legacy/src/animator/SkDrawColor.h b/legacy/src/animator/SkDrawColor.h
new file mode 100644
index 0000000..29c73c3
--- /dev/null
+++ b/legacy/src/animator/SkDrawColor.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 SkDrawColor_DEFINED
+#define SkDrawColor_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkColor.h"
+
+class SkDrawColor : public SkPaintPart {
+    DECLARE_DRAW_MEMBER_INFO(Color);
+    SkDrawColor();
+    virtual bool add();
+    virtual void dirty();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkColor getColor();
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual SkDisplayable* getParent() const;
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual bool setParent(SkDisplayable* parent);
+    virtual bool setProperty(int index, SkScriptValue&);
+protected:
+    SkColor color;
+    SkScalar fHue;
+    SkScalar fSaturation;
+    SkScalar fValue;
+    SkBool fDirty;
+private:
+    friend class SkGradient;
+    typedef SkPaintPart INHERITED;
+};
+
+#endif // SkDrawColor_DEFINED
diff --git a/legacy/src/animator/SkDrawDash.cpp b/legacy/src/animator/SkDrawDash.cpp
new file mode 100644
index 0000000..0d93293
--- /dev/null
+++ b/legacy/src/animator/SkDrawDash.cpp
@@ -0,0 +1,36 @@
+
+/*
+ * 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 "SkDrawDash.h"
+#include "SkDashPathEffect.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDash::fInfo[] = {
+    SK_MEMBER_ARRAY(intervals, Float),
+    SK_MEMBER(phase, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDash);
+
+SkDash::SkDash() : phase(0) {
+}
+
+SkDash::~SkDash() {
+}
+
+SkPathEffect* SkDash::getPathEffect() {
+    int count = intervals.count();
+    if (count == 0)
+        return NULL;
+    return new SkDashPathEffect(intervals.begin(), count, phase);
+}
+
diff --git a/legacy/src/animator/SkDrawDash.h b/legacy/src/animator/SkDrawDash.h
new file mode 100644
index 0000000..483d2a3
--- /dev/null
+++ b/legacy/src/animator/SkDrawDash.h
@@ -0,0 +1,27 @@
+
+/*
+ * 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 SkDrawDash_DEFINED
+#define SkDrawDash_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkIntArray.h"
+
+class SkDash : public SkDrawPathEffect {
+    DECLARE_MEMBER_INFO(Dash);
+    SkDash();
+    virtual ~SkDash();
+    virtual SkPathEffect* getPathEffect();
+private:
+    SkTDScalarArray intervals;
+    SkScalar phase;
+};
+
+#endif // SkDrawDash_DEFINED
+
diff --git a/legacy/src/animator/SkDrawDiscrete.cpp b/legacy/src/animator/SkDrawDiscrete.cpp
new file mode 100644
index 0000000..655c83d
--- /dev/null
+++ b/legacy/src/animator/SkDrawDiscrete.cpp
@@ -0,0 +1,35 @@
+
+/*
+ * 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 "SkDrawDiscrete.h"
+#include "SkAnimateMaker.h"
+#include "SkPaint.h"
+#include "SkDiscretePathEffect.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDiscrete::fInfo[] = {
+    SK_MEMBER(deviation, Float),
+    SK_MEMBER(segLength, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDiscrete);
+
+SkDiscrete::SkDiscrete() : deviation(0), segLength(0) {
+}
+
+SkPathEffect* SkDiscrete::getPathEffect() {
+    if (deviation <= 0 || segLength <= 0)
+        return NULL;
+    else
+        return new SkDiscretePathEffect(segLength, deviation);
+}
+
diff --git a/legacy/src/animator/SkDrawDiscrete.h b/legacy/src/animator/SkDrawDiscrete.h
new file mode 100644
index 0000000..bd33d2f
--- /dev/null
+++ b/legacy/src/animator/SkDrawDiscrete.h
@@ -0,0 +1,24 @@
+
+/*
+ * 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 SkDrawDiscrete_DEFINED
+#define SkDrawDiscrete_DEFINED
+
+#include "SkPaintParts.h"
+
+class SkDiscrete : public SkDrawPathEffect {
+    DECLARE_MEMBER_INFO(Discrete);
+    SkDiscrete();
+    virtual SkPathEffect* getPathEffect();
+private:
+    SkScalar deviation;
+    SkScalar segLength;
+};
+
+#endif //SkDrawDiscrete_DEFINED
diff --git a/legacy/src/animator/SkDrawEmboss.cpp b/legacy/src/animator/SkDrawEmboss.cpp
new file mode 100644
index 0000000..2284504
--- /dev/null
+++ b/legacy/src/animator/SkDrawEmboss.cpp
@@ -0,0 +1,34 @@
+
+/*
+ * 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 "SkDrawEmboss.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawEmboss::fInfo[] = {
+    SK_MEMBER(ambient, Float),
+    SK_MEMBER_ARRAY(direction, Float),
+    SK_MEMBER(radius, Float),
+    SK_MEMBER(specular, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawEmboss);
+
+SkDrawEmboss::SkDrawEmboss() : radius(-1) { 
+    direction.setCount(3);
+}
+
+SkMaskFilter* SkDrawEmboss::getMaskFilter() {
+    if (radius < 0 || direction.count() !=3)
+        return NULL;
+    return SkBlurMaskFilter::CreateEmboss(direction.begin(), ambient, specular, radius);
+}
+
diff --git a/legacy/src/animator/SkDrawEmboss.h b/legacy/src/animator/SkDrawEmboss.h
new file mode 100644
index 0000000..50ce71a
--- /dev/null
+++ b/legacy/src/animator/SkDrawEmboss.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 SkDrawEmboss_DEFINED
+#define SkDrawEmboss_DEFINED
+
+#include "SkDrawBlur.h"
+
+class SkDrawEmboss : public SkDrawMaskFilter {
+    DECLARE_DRAW_MEMBER_INFO(Emboss);
+    SkDrawEmboss();
+    virtual SkMaskFilter* getMaskFilter();
+protected:
+    SkTDScalarArray direction;
+    SkScalar radius, ambient, specular;
+};
+
+#endif // SkDrawEmboss_DEFINED
+
diff --git a/legacy/src/animator/SkDrawExtraPathEffect.cpp b/legacy/src/animator/SkDrawExtraPathEffect.cpp
new file mode 100644
index 0000000..12e0368
--- /dev/null
+++ b/legacy/src/animator/SkDrawExtraPathEffect.cpp
@@ -0,0 +1,511 @@
+
+/*
+ * 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 "SkDrawExtraPathEffect.h"
+#include "SkDrawPath.h"
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkMemberInfo.h"
+#include "SkPaintParts.h"
+#include "SkPathEffect.h"
+#include "SkCornerPathEffect.h"
+
+#include "SkDashPathEffect.h"
+
+class SkDrawShapePathEffect : public SkDrawPathEffect {
+    DECLARE_PRIVATE_MEMBER_INFO(DrawShapePathEffect);
+    SkDrawShapePathEffect();
+    virtual ~SkDrawShapePathEffect();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* );
+    virtual SkPathEffect* getPathEffect();
+protected:
+    SkDrawable* addPath;
+    SkDrawable* addMatrix;
+    SkDrawPath* path;
+    SkPathEffect* fPathEffect;
+    friend class SkShape1DPathEffect;
+    friend class SkShape2DPathEffect;
+};
+
+class SkDrawShape1DPathEffect : public SkDrawShapePathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape1DPathEffect);
+    SkDrawShape1DPathEffect(SkDisplayTypes );
+    virtual ~SkDrawShape1DPathEffect();
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    SkString phase;
+    SkString spacing;
+    friend class SkShape1DPathEffect;
+    typedef SkDrawShapePathEffect INHERITED;
+};
+
+class SkDrawShape2DPathEffect : public SkDrawShapePathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape2DPathEffect);
+    SkDrawShape2DPathEffect(SkDisplayTypes );
+    virtual ~SkDrawShape2DPathEffect();
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    SkDrawMatrix* matrix;
+    friend class SkShape2DPathEffect;
+    typedef SkDrawShapePathEffect INHERITED;
+};
+
+class SkDrawComposePathEffect : public SkDrawPathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawComposePathEffect);
+    SkDrawComposePathEffect(SkDisplayTypes );
+    virtual ~SkDrawComposePathEffect();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* );
+    virtual SkPathEffect* getPathEffect();
+    virtual bool isPaint() const;
+private:
+    SkDrawPathEffect* effect1;
+    SkDrawPathEffect* effect2;
+};
+
+class SkDrawCornerPathEffect : public SkDrawPathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawCornerPathEffect);
+    SkDrawCornerPathEffect(SkDisplayTypes );
+    virtual ~SkDrawCornerPathEffect();
+    virtual SkPathEffect* getPathEffect();
+private:
+    SkScalar radius;
+};
+
+//////////// SkShape1DPathEffect
+
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayApply.h"
+#include "SkDrawMatrix.h"
+#include "SkPaint.h"
+
+class SkShape1DPathEffect : public Sk1DPathEffect {
+public:
+    SkShape1DPathEffect(SkDrawShape1DPathEffect* draw, SkAnimateMaker* maker) :
+        fDraw(draw), fMaker(maker) {
+    }
+
+protected:
+    virtual SkScalar begin(SkScalar contourLength)
+    {
+        SkScriptValue value;
+        SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
+        engine.propertyCallBack(GetContourLength, &contourLength);
+        value.fOperand.fScalar = 0;
+        engine.evaluate(fDraw->phase.c_str(), &value, SkType_Float);
+        return value.fOperand.fScalar;
+    }
+
+    virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure& )
+    {
+        fMaker->setExtraPropertyCallBack(fDraw->fType, GetDistance, &distance);
+        SkDrawPath* drawPath = NULL;
+        if (fDraw->addPath->isPath()) {
+            drawPath = (SkDrawPath*) fDraw->addPath;
+        } else {
+            SkApply* apply = (SkApply*) fDraw->addPath;
+            apply->refresh(*fMaker);
+            apply->activate(*fMaker);
+            apply->interpolate(*fMaker, SkScalarMulRound(distance, 1000));
+            drawPath = (SkDrawPath*) apply->getScope();
+        }
+        SkMatrix m;
+        m.reset();
+        if (fDraw->addMatrix) {
+            SkDrawMatrix* matrix;
+            if (fDraw->addMatrix->getType() == SkType_Matrix)
+                matrix = (SkDrawMatrix*) fDraw->addMatrix;
+            else {
+                SkApply* apply = (SkApply*) fDraw->addMatrix;
+                apply->refresh(*fMaker);
+                apply->activate(*fMaker);
+                apply->interpolate(*fMaker, SkScalarMulRound(distance, 1000));
+                matrix = (SkDrawMatrix*) apply->getScope();
+            }
+        }
+        SkScalar result = 0;
+        SkAnimatorScript::EvaluateFloat(*fMaker, NULL, fDraw->spacing.c_str(), &result);
+        if (drawPath)
+            dst->addPath(drawPath->getPath(), m);
+        fMaker->clearExtraPropertyCallBack(fDraw->fType);
+        return result;
+    }
+
+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;
+            value->fType = SkType_Float;
+            return true;
+        }
+        return false;
+    }
+
+    static bool GetDistance(const char* token, size_t len, void* dist, SkScriptValue* value) {
+        if (SK_LITERAL_STR_EQUAL("distance", token, len)) {
+            value->fOperand.fScalar = *(SkScalar*) dist;
+            value->fType = SkType_Float;
+            return true;
+        }
+        return false;
+    }
+
+    SkDrawShape1DPathEffect* fDraw;
+    SkAnimateMaker* fMaker;
+};
+
+//////////// SkDrawShapePathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShapePathEffect::fInfo[] = {
+    SK_MEMBER(addMatrix, Drawable), // either matrix or apply
+    SK_MEMBER(addPath, Drawable),   // either path or apply
+    SK_MEMBER(path, Path),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShapePathEffect);
+
+SkDrawShapePathEffect::SkDrawShapePathEffect() :
+    addPath(NULL), addMatrix(NULL), path(NULL), fPathEffect(NULL) {
+}
+
+SkDrawShapePathEffect::~SkDrawShapePathEffect() {
+    SkSafeUnref(fPathEffect);
+}
+
+bool SkDrawShapePathEffect::add(SkAnimateMaker& , SkDisplayable* child) {
+    path = (SkDrawPath*) child;
+    return true;
+}
+
+SkPathEffect* SkDrawShapePathEffect::getPathEffect() {
+    fPathEffect->ref();
+    return fPathEffect;
+}
+
+//////////// SkDrawShape1DPathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShape1DPathEffect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(phase, String),
+    SK_MEMBER(spacing, String),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShape1DPathEffect);
+
+SkDrawShape1DPathEffect::SkDrawShape1DPathEffect(SkDisplayTypes type) : fType(type) {
+}
+
+SkDrawShape1DPathEffect::~SkDrawShape1DPathEffect() {
+}
+
+void SkDrawShape1DPathEffect::onEndElement(SkAnimateMaker& maker) {
+    if (addPath == NULL || (addPath->isPath() == false && addPath->isApply() == false))
+        maker.setErrorCode(SkDisplayXMLParserError::kUnknownError); // !!! add error
+    else
+        fPathEffect = new SkShape1DPathEffect(this, &maker);
+}
+
+////////// SkShape2DPathEffect
+
+class SkShape2DPathEffect : public Sk2DPathEffect {
+public:
+    SkShape2DPathEffect(SkDrawShape2DPathEffect* draw, SkAnimateMaker* maker,
+        const SkMatrix& matrix) : Sk2DPathEffect(matrix), fDraw(draw), fMaker(maker) {
+    }
+
+protected:
+    virtual void begin(const SkIRect& uvBounds, SkPath* )
+    {
+        fUVBounds.set(SkIntToScalar(uvBounds.fLeft), SkIntToScalar(uvBounds.fTop),
+            SkIntToScalar(uvBounds.fRight), SkIntToScalar(uvBounds.fBottom));
+    }
+
+    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst)
+    {
+        fLoc = loc;
+        fU = u;
+        fV = v;
+        SkDrawPath* drawPath;
+        fMaker->setExtraPropertyCallBack(fDraw->fType, Get2D, this);
+        if (fDraw->addPath->isPath()) {
+            drawPath = (SkDrawPath*) fDraw->addPath;
+        } else {
+            SkApply* apply = (SkApply*) fDraw->addPath;
+            apply->refresh(*fMaker);
+            apply->activate(*fMaker);
+            apply->interpolate(*fMaker, v);
+            drawPath = (SkDrawPath*) apply->getScope();
+        }
+        if (drawPath == NULL)
+            goto clearCallBack;
+        if (fDraw->matrix) {
+            SkDrawMatrix* matrix;
+            if (fDraw->matrix->getType() == SkType_Matrix)
+                matrix = (SkDrawMatrix*) fDraw->matrix;
+            else {
+                SkApply* apply = (SkApply*) fDraw->matrix;
+                apply->activate(*fMaker);
+                apply->interpolate(*fMaker, v);
+                matrix = (SkDrawMatrix*) apply->getScope();
+            }
+            if (matrix) {
+                dst->addPath(drawPath->getPath(), matrix->getMatrix());
+                goto clearCallBack;
+            }
+        }
+        dst->addPath(drawPath->getPath());
+clearCallBack:
+        fMaker->clearExtraPropertyCallBack(fDraw->fType);
+    }
+
+private:
+
+    static bool Get2D(const char* token, size_t len, void* s2D, SkScriptValue* value) {
+        static const char match[] = "locX|locY|left|top|right|bottom|u|v" ;
+        SkShape2DPathEffect* shape2D = (SkShape2DPathEffect*) s2D;
+        int index;
+        if (SkAnimatorScript::MapEnums(match, token, len, &index) == false)
+            return false;
+        SkASSERT((sizeof(SkPoint) +     sizeof(SkRect)) / sizeof(SkScalar) == 6);
+        if (index < 6) {
+            value->fType = SkType_Float;
+            value->fOperand.fScalar = (&shape2D->fLoc.fX)[index];
+        } else {
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = (&shape2D->fU)[index - 6];
+        }
+        return true;
+    }
+
+    SkPoint fLoc;
+    SkRect fUVBounds;
+    int32_t fU;
+    int32_t fV;
+    SkDrawShape2DPathEffect* fDraw;
+    SkAnimateMaker* fMaker;
+
+    // illegal
+    SkShape2DPathEffect(const SkShape2DPathEffect&);
+    SkShape2DPathEffect& operator=(const SkShape2DPathEffect&);
+};
+
+////////// SkDrawShape2DPathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShape2DPathEffect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(matrix, Matrix)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShape2DPathEffect);
+
+SkDrawShape2DPathEffect::SkDrawShape2DPathEffect(SkDisplayTypes type) : fType(type) {
+}
+
+SkDrawShape2DPathEffect::~SkDrawShape2DPathEffect() {
+}
+
+void SkDrawShape2DPathEffect::onEndElement(SkAnimateMaker& maker) {
+    if (addPath == NULL || (addPath->isPath() == false && addPath->isApply() == false) ||
+            matrix == NULL)
+        maker.setErrorCode(SkDisplayXMLParserError::kUnknownError); // !!! add error
+    else
+        fPathEffect = new SkShape2DPathEffect(this, &maker, matrix->getMatrix());
+}
+
+////////// SkDrawComposePathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawComposePathEffect::fInfo[] = {
+    SK_MEMBER(effect1, PathEffect),
+    SK_MEMBER(effect2, PathEffect)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawComposePathEffect);
+
+SkDrawComposePathEffect::SkDrawComposePathEffect(SkDisplayTypes type) : fType(type),
+    effect1(NULL), effect2(NULL) {
+}
+
+SkDrawComposePathEffect::~SkDrawComposePathEffect() {
+    delete effect1;
+    delete effect2;
+}
+
+bool SkDrawComposePathEffect::add(SkAnimateMaker& , SkDisplayable* child) {
+    if (effect1 == NULL)
+        effect1 = (SkDrawPathEffect*) child;
+    else
+        effect2 = (SkDrawPathEffect*) child;
+    return true;
+}
+
+SkPathEffect* SkDrawComposePathEffect::getPathEffect() {
+    SkPathEffect* e1 = effect1->getPathEffect();
+    SkPathEffect* e2 = effect2->getPathEffect();
+    SkPathEffect* composite = new SkComposePathEffect(e1, e2);
+    e1->unref();
+    e2->unref();
+    return composite;
+}
+
+bool SkDrawComposePathEffect::isPaint() const {
+    return true;
+}
+
+//////////// SkDrawCornerPathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawCornerPathEffect::fInfo[] = {
+    SK_MEMBER(radius, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawCornerPathEffect);
+
+SkDrawCornerPathEffect::SkDrawCornerPathEffect(SkDisplayTypes type):
+    fType(type), radius(0) {
+}
+
+SkDrawCornerPathEffect::~SkDrawCornerPathEffect() {
+}
+
+SkPathEffect* SkDrawCornerPathEffect::getPathEffect() {
+    return new SkCornerPathEffect(radius);
+}
+
+/////////
+
+#include "SkExtras.h"
+
+const char kDrawShape1DPathEffectName[] = "pathEffect:shape1D";
+const char kDrawShape2DPathEffectName[] = "pathEffect:shape2D";
+const char kDrawComposePathEffectName[] = "pathEffect:compose";
+const char kDrawCornerPathEffectName[]  = "pathEffect:corner";
+
+class SkExtraPathEffects : public SkExtras {
+public:
+    SkExtraPathEffects(SkAnimator* animator) :
+            skDrawShape1DPathEffectType(SkType_Unknown),
+            skDrawShape2DPathEffectType(SkType_Unknown),
+            skDrawComposePathEffectType(SkType_Unknown),
+            skDrawCornerPathEffectType(SkType_Unknown) {
+    }
+
+    virtual SkDisplayable* createInstance(SkDisplayTypes type) {
+        SkDisplayable* result = NULL;
+        if (skDrawShape1DPathEffectType == type)
+            result = new SkDrawShape1DPathEffect(type);
+        else if (skDrawShape2DPathEffectType == type)
+            result = new SkDrawShape2DPathEffect(type);
+        else if (skDrawComposePathEffectType == type)
+            result = new SkDrawComposePathEffect(type);
+        else if (skDrawCornerPathEffectType == type)
+            result = new SkDrawCornerPathEffect(type);
+        return result;
+    }
+
+    virtual bool definesType(SkDisplayTypes type) {
+        return type == skDrawShape1DPathEffectType ||
+            type == skDrawShape2DPathEffectType ||
+            type == skDrawComposePathEffectType ||
+            type == skDrawCornerPathEffectType;
+    }
+
+#if SK_USE_CONDENSED_INFO == 0
+    virtual const SkMemberInfo* getMembers(SkDisplayTypes type, int* infoCountPtr) {
+        const SkMemberInfo* info = NULL;
+        int infoCount = 0;
+        if (skDrawShape1DPathEffectType == type) {
+            info = SkDrawShape1DPathEffect::fInfo;
+            infoCount = SkDrawShape1DPathEffect::fInfoCount;
+        } else if (skDrawShape2DPathEffectType == type) {
+            info = SkDrawShape2DPathEffect::fInfo;
+            infoCount = SkDrawShape2DPathEffect::fInfoCount;
+        } else if (skDrawComposePathEffectType == type) {
+            info = SkDrawComposePathEffect::fInfo;
+            infoCount = SkDrawShape1DPathEffect::fInfoCount;
+        } else if (skDrawCornerPathEffectType == type) {
+            info = SkDrawCornerPathEffect::fInfo;
+            infoCount = SkDrawCornerPathEffect::fInfoCount;
+        }
+        if (infoCountPtr)
+            *infoCountPtr = infoCount;
+        return info;
+    }
+#endif
+
+#ifdef SK_DEBUG
+    virtual const char* getName(SkDisplayTypes type) {
+        if (skDrawShape1DPathEffectType == type)
+            return kDrawShape1DPathEffectName;
+        else if (skDrawShape2DPathEffectType == type)
+            return kDrawShape2DPathEffectName;
+        else if (skDrawComposePathEffectType == type)
+            return kDrawComposePathEffectName;
+        else if (skDrawCornerPathEffectType == type)
+            return kDrawCornerPathEffectName;
+        return NULL;
+    }
+#endif
+
+    virtual SkDisplayTypes getType(const char name[], size_t len ) {
+        SkDisplayTypes* type = NULL;
+        if (SK_LITERAL_STR_EQUAL(kDrawShape1DPathEffectName, name, len))
+            type = &skDrawShape1DPathEffectType;
+        else if (SK_LITERAL_STR_EQUAL(kDrawShape2DPathEffectName, name, len))
+            type = &skDrawShape2DPathEffectType;
+        else if (SK_LITERAL_STR_EQUAL(kDrawComposePathEffectName, name, len))
+            type = &skDrawComposePathEffectType;
+        else if (SK_LITERAL_STR_EQUAL(kDrawCornerPathEffectName, name, len))
+            type = &skDrawCornerPathEffectType;
+        if (type) {
+            if (*type == SkType_Unknown)
+                *type = SkDisplayType::RegisterNewType();
+            return *type;
+        }
+        return SkType_Unknown;
+    }
+
+private:
+    SkDisplayTypes skDrawShape1DPathEffectType;
+    SkDisplayTypes skDrawShape2DPathEffectType;
+    SkDisplayTypes skDrawComposePathEffectType;
+    SkDisplayTypes skDrawCornerPathEffectType;
+};
+
+
+void InitializeSkExtraPathEffects(SkAnimator* animator) {
+    animator->addExtras(new SkExtraPathEffects(animator));
+}
+
+////////////////
+
+
+SkExtras::SkExtras() : fExtraCallBack(NULL), fExtraStorage(NULL) {
+}
diff --git a/legacy/src/animator/SkDrawFull.cpp b/legacy/src/animator/SkDrawFull.cpp
new file mode 100644
index 0000000..762b3da
--- /dev/null
+++ b/legacy/src/animator/SkDrawFull.cpp
@@ -0,0 +1,19 @@
+
+/*
+ * 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 "SkDrawFull.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+
+bool SkFull::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawPaint(*maker.fPaint);
+    return false;
+}
+
diff --git a/legacy/src/animator/SkDrawFull.h b/legacy/src/animator/SkDrawFull.h
new file mode 100644
index 0000000..a2dcf49
--- /dev/null
+++ b/legacy/src/animator/SkDrawFull.h
@@ -0,0 +1,22 @@
+
+/*
+ * 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 SkDrawFull_DEFINED
+#define SkDrawFull_DEFINED
+
+#include "SkBoundable.h"
+
+class SkFull : public SkBoundable {
+    DECLARE_EMPTY_MEMBER_INFO(Full);
+    virtual bool draw(SkAnimateMaker& );
+private:
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkDrawFull_DEFINED
diff --git a/legacy/src/animator/SkDrawGradient.cpp b/legacy/src/animator/SkDrawGradient.cpp
new file mode 100644
index 0000000..37fc7e8
--- /dev/null
+++ b/legacy/src/animator/SkDrawGradient.cpp
@@ -0,0 +1,227 @@
+
+/*
+ * 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 "SkDrawGradient.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkGradientShader.h"
+#include "SkUnitMapper.h"
+
+SkScalar SkUnitToScalar(U16CPU x) {
+#ifdef SK_SCALAR_IS_FLOAT
+    return x / 65535.0f;
+#else
+    return x + (x >> 8);
+#endif
+}
+
+U16CPU SkScalarToUnit(SkScalar x) {
+    SkScalar pin =  SkScalarPin(x, 0, SK_Scalar1);
+#ifdef SK_SCALAR_IS_FLOAT
+    return (int) (pin * 65535.0f);
+#else
+    return pin - (pin >= 32768);
+#endif
+}
+
+class SkGradientUnitMapper : public SkUnitMapper {
+public:
+    SkGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) {
+    }
+    
+    // overrides for SkFlattenable
+    virtual Factory getFactory() { return NULL; }
+    
+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)) 
+            x = SkScalarToUnit(value.fOperand.fScalar);
+        return x;
+    }
+
+    static bool GetUnitValue(const char* token, size_t len, void* unitPtr, SkScriptValue* value) {
+        if (SK_LITERAL_STR_EQUAL("unit", token, len)) {
+            value->fOperand.fScalar = *(SkScalar*) unitPtr;
+            value->fType = SkType_Float;
+            return true;
+        }
+        return false;
+    }
+
+    SkAnimateMaker* fMaker;
+    const char* fScript;
+    SkScalar fUnit;
+};
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ARRAY(offsets, Float),
+    SK_MEMBER(unitMapper, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkGradient);
+
+SkGradient::SkGradient() : fUnitMapper(NULL) {
+}
+
+SkGradient::~SkGradient() {
+    for (int index = 0; index < fDrawColors.count(); index++) 
+        delete fDrawColors[index];
+    delete fUnitMapper;
+}
+
+bool SkGradient::add(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child);
+    if (child->isColor()) {
+        SkDrawColor* color = (SkDrawColor*) child;
+        *fDrawColors.append() = color;
+        return true;
+    }
+    return false;
+}
+
+int SkGradient::addPrelude() {
+    int count = fDrawColors.count();
+    fColors.setCount(count);
+    for (int index = 0; index < count; index++) 
+        fColors[index] = fDrawColors[index]->color;
+    return count;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkGradient::dumpRest(SkAnimateMaker* maker) {
+    dumpAttrs(maker);
+    //can a gradient have no colors?
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    for (SkDrawColor** ptr = fDrawColors.begin(); ptr < fDrawColors.end(); ptr++) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        SkDrawColor* color = *ptr;
+        color->dump(maker);
+    }
+    SkDisplayList::fIndent -= 4;    
+    dumpChildren(maker, closedYet); //dumps the matrix if it has one
+}
+#endif
+
+void SkGradient::onEndElement(SkAnimateMaker& maker) {
+    if (offsets.count() != 0) {
+        if (offsets.count() != fDrawColors.count()) {
+            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsDontMatchColors);
+            return;
+        }
+        if (offsets[0] != 0) {
+            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustStartWithZero);
+            return;
+        }
+        if (offsets[offsets.count()-1] != SK_Scalar1) {
+            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustEndWithOne);
+            return;
+        }
+        for (int i = 1; i < offsets.count(); i++) {
+            if (offsets[i] <= offsets[i-1]) {
+                maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustIncrease);
+                return;
+            }
+            if (offsets[i] > SK_Scalar1) {
+                maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustBeNoMoreThanOne);
+                return;
+            }
+        }
+    }
+    if (unitMapper.size() > 0) 
+        fUnitMapper = new SkGradientUnitMapper(&maker, unitMapper.c_str());
+    INHERITED::onEndElement(maker);
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkLinearGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ARRAY(points, Float),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkLinearGradient);
+
+SkLinearGradient::SkLinearGradient() { 
+}
+
+void SkLinearGradient::onEndElement(SkAnimateMaker& maker)
+{
+    if (points.count() != 4)
+        maker.setErrorCode(SkDisplayXMLParserError::kGradientPointsLengthMustBeFour);
+    INHERITED::onEndElement(maker);
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkLinearGradient::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpRest(maker);
+    }
+#endif
+
+SkShader* SkLinearGradient::getShader() {
+    if (addPrelude() == 0 || points.count() != 4)
+        return NULL;
+    SkShader* shader = SkGradientShader::CreateLinear((SkPoint*)points.begin(),
+        fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
+    SkAutoTDelete<SkShader> autoDel(shader);
+    addPostlude(shader);
+    (void)autoDel.detach();
+    return shader;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRadialGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(center, Point),
+    SK_MEMBER(radius, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRadialGradient);
+
+SkRadialGradient::SkRadialGradient() : radius(0) { 
+    center.set(0, 0); 
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkRadialGradient::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpRest(maker);
+}
+#endif
+
+SkShader* SkRadialGradient::getShader() {
+    if (addPrelude() == 0)
+        return NULL;
+    SkShader* shader = SkGradientShader::CreateRadial(center,
+        radius, fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
+    SkAutoTDelete<SkShader> autoDel(shader);
+    addPostlude(shader);
+    (void)autoDel.detach();
+    return shader;
+}
diff --git a/legacy/src/animator/SkDrawGradient.h b/legacy/src/animator/SkDrawGradient.h
new file mode 100644
index 0000000..251e975
--- /dev/null
+++ b/legacy/src/animator/SkDrawGradient.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 SkDrawGradient_DEFINED
+#define SkDrawGradient_DEFINED
+
+#include "SkDrawColor.h"
+#include "SkDrawShader.h"
+#include "SkIntArray.h"
+
+class SkUnitMapper;
+
+class SkGradient : public SkDrawShader {
+    DECLARE_PRIVATE_MEMBER_INFO(Gradient);
+    SkGradient();
+    virtual ~SkGradient();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+#ifdef SK_DUMP_ENABLED
+    virtual void dumpRest(SkAnimateMaker*);
+#endif    
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkTDScalarArray offsets;
+    SkString unitMapper;
+    SkTDColorArray fColors;
+    SkTDDrawColorArray fDrawColors;
+    SkUnitMapper* fUnitMapper;
+    int addPrelude();
+private:
+    typedef SkDrawShader INHERITED;
+};
+
+class SkLinearGradient : public SkGradient {
+    DECLARE_MEMBER_INFO(LinearGradient);
+    SkLinearGradient();
+    virtual void onEndElement(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker*);
+#endif
+    virtual SkShader* getShader();
+protected:
+    SkTDScalarArray points;
+private:
+    typedef SkGradient INHERITED;
+};
+
+class SkRadialGradient : public SkGradient {
+    DECLARE_MEMBER_INFO(RadialGradient);
+    SkRadialGradient();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker*);
+#endif    
+    virtual SkShader* getShader();
+protected:
+    SkPoint center;
+    SkScalar radius;
+private:
+    typedef SkGradient INHERITED;
+};
+
+#endif // SkDrawGradient_DEFINED
+
diff --git a/legacy/src/animator/SkDrawGroup.cpp b/legacy/src/animator/SkDrawGroup.cpp
new file mode 100644
index 0000000..939bd9f
--- /dev/null
+++ b/legacy/src/animator/SkDrawGroup.cpp
@@ -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.
+ */
+
+
+#include "SkDrawGroup.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkCanvas.h"
+#include "SkDisplayApply.h"
+#include "SkPaint.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkGroup::fInfo[] = {
+    SK_MEMBER(condition, String),
+    SK_MEMBER(enableCondition, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkGroup);
+
+SkGroup::SkGroup() : fParentList(NULL), fOriginal(NULL) {
+}
+
+SkGroup::~SkGroup() {
+    if (fOriginal)  // has been copied
+        return;
+    int index = 0;
+    int max = fCopies.count() << 5;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        if (index >= max || markedForDelete(index))
+            delete *ptr;
+//      else {
+//          SkApply* apply = (SkApply*) *ptr;
+//          SkASSERT(apply->isApply());
+//          SkASSERT(apply->getScope());
+//          delete apply->getScope();
+//      }
+        index++;
+    }
+}
+
+bool SkGroup::add(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child); 
+//  SkASSERT(child->isDrawable());
+    *fChildren.append() = (SkDrawable*) child;
+    if (child->isGroup()) {
+        SkGroup* groupie = (SkGroup*) child;
+        SkASSERT(groupie->fParentList == NULL);
+        groupie->fParentList = &fChildren;
+    }
+    return true;
+}
+
+bool SkGroup::contains(SkDisplayable* match) {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable == match || drawable->contains(match))
+            return true;
+    }
+    return false;
+}
+
+SkGroup* SkGroup::copy() {
+    SkGroup* result = new SkGroup();
+    result->fOriginal = this;
+    result->fChildren = fChildren;
+    return result;
+}
+
+SkBool SkGroup::copySet(int index) {
+    return (fCopies[index >> 5] & 1 << (index & 0x1f)) != 0;
+}
+
+SkDisplayable* SkGroup::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayable* copy = INHERITED::deepCopy(maker);
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDisplayable* displayable = (SkDisplayable*)*ptr;
+        SkDisplayable* deeperCopy = displayable->deepCopy(maker);
+        ((SkGroup*)copy)->add(*maker, deeperCopy);
+    }
+    return copy;
+}
+
+bool SkGroup::doEvent(SkDisplayEvent::Kind kind, SkEventState* state) {
+    bool handled = false;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        handled |= drawable->doEvent(kind, state);
+    }
+    return handled;
+}
+
+bool SkGroup::draw(SkAnimateMaker& maker) {
+    bool conditionTrue = ifCondition(maker, this, condition);
+    bool result = false;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        if (conditionTrue == false) {
+            if (drawable->isApply())
+                ((SkApply*) drawable)->disable();
+            continue;
+        }
+        maker.validate();
+        result |= drawable->draw(maker);
+        maker.validate();
+    }
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkGroup::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (condition.size() > 0)
+        SkDebugf("condition=\"%s\" ", condition.c_str());
+    if (enableCondition.size() > 0)
+        SkDebugf("enableCondition=\"%s\" ", enableCondition.c_str());
+    dumpDrawables(maker);
+}
+
+void SkGroup::dumpDrawables(SkAnimateMaker* maker) {
+    SkDisplayList::fIndent += 4;
+    int save = SkDisplayList::fDumpIndex;
+    SkDisplayList::fDumpIndex = 0;
+    bool closedYet = false;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        if (closedYet == false) {
+            closedYet = true;
+            SkDebugf(">\n");
+        }
+        SkDrawable* drawable = *ptr;
+        drawable->dump(maker);
+        SkDisplayList::fDumpIndex++;
+    }
+    SkDisplayList::fIndent -= 4;
+    SkDisplayList::fDumpIndex = save;
+    if (closedYet) //we had children, now it's time to close the group
+        dumpEnd(maker);
+    else    //no children
+        SkDebugf("/>\n");
+}
+
+void SkGroup::dumpEvents() {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        drawable->dumpEvents();
+    }
+}
+#endif
+
+bool SkGroup::enable(SkAnimateMaker& maker ) {
+    reset();
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (ifCondition(maker, drawable, enableCondition) == false)
+            continue;
+        drawable->enable(maker);
+    }
+    return true;    // skip add; already added so that scope is findable by children
+}
+
+int SkGroup::findGroup(SkDrawable* match,  SkTDDrawableArray** list,
+                 SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList) {
+    *list = &fChildren;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isGroup()) {
+            SkGroup* childGroup = (SkGroup*) drawable;
+            if (childGroup->fOriginal == match)
+                goto foundMatch;
+        }
+        if (drawable == match) {
+foundMatch:
+            *parent = this;
+            return (int) (ptr - fChildren.begin());
+        }
+    }
+    *grandList = &fChildren;
+    return SkDisplayList::SearchForMatch(match, list, parent, found, grandList);
+}
+
+bool SkGroup::hasEnable() const {
+    return true;
+}
+
+bool SkGroup::ifCondition(SkAnimateMaker& maker, SkDrawable* drawable,
+        SkString& conditionString) {
+    if (conditionString.size() == 0)
+        return true;
+    int32_t result;
+    bool success = SkAnimatorScript::EvaluateInt(maker, this, conditionString.c_str(), &result);
+#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpGConditions) {
+        SkDebugf("group: ");
+        dumpBase(&maker);
+        SkDebugf("condition=%s ", conditionString.c_str());
+        if (success == false)
+            SkDebugf("(script failed)\n");
+        else
+            SkDebugf("success=%s\n", result != 0 ? "true" : "false");
+    }
+#endif
+    return success && result != 0;
+}
+
+void SkGroup::initialize() {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        drawable->initialize();
+    }
+}
+
+void SkGroup::markCopyClear(int index) {
+    if (index < 0)
+        index = fChildren.count();
+    fCopies[index >> 5] &= ~(1 << (index & 0x1f));
+}
+
+void SkGroup::markCopySet(int index) {
+    if (index < 0)
+        index = fChildren.count();
+    fCopies[index >> 5] |= 1 << (index & 0x1f);
+}
+
+void SkGroup::markCopySize(int index) {
+    if (index < 0)
+        index = fChildren.count() + 1;
+    int oldLongs = fCopies.count();
+    int newLongs = (index >> 5) + 1;
+    if (oldLongs < newLongs) {
+        fCopies.setCount(newLongs);
+        memset(&fCopies[oldLongs], 0, (newLongs - oldLongs) << 2);
+    }
+}
+
+void SkGroup::reset() {
+    if (fOriginal)  // has been copied
+        return;
+    int index = 0;
+    int max = fCopies.count() << 5;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        if (index >= max || copySet(index) == false)
+            continue;
+        SkApply* apply = (SkApply*) *ptr;
+        SkASSERT(apply->isApply());
+        SkASSERT(apply->getScope());
+        *ptr = apply->getScope();
+        markCopyClear(index);
+        index++;
+    }
+}
+
+bool SkGroup::resolveIDs(SkAnimateMaker& maker, SkDisplayable* orig, SkApply* apply) {
+    SkGroup* original = (SkGroup*) orig;
+    SkTDDrawableArray& originalChildren = original->fChildren;
+    SkDrawable** originalPtr = originalChildren.begin();
+    SkDrawable** ptr = fChildren.begin();
+    SkDrawable** end = fChildren.end();
+    SkDrawable** origChild = ((SkGroup*) orig)->fChildren.begin();
+    while (ptr < end) {
+        SkDrawable* drawable = *ptr++;
+        maker.resolveID(drawable, *origChild++);
+        if (drawable->resolveIDs(maker, *originalPtr++, apply) == true)
+            return true; // failed
+    }
+    return false;
+}
+
+void SkGroup::setSteps(int steps) {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        drawable->setSteps(steps);
+    }
+}
+
+#ifdef SK_DEBUG
+void SkGroup::validate() {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        drawable->validate();
+    }
+}
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSave::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSave);
+
+bool SkSave::draw(SkAnimateMaker& maker) {
+    maker.fCanvas->save();
+    SkPaint* save = maker.fPaint;
+    SkPaint local = SkPaint(*maker.fPaint);
+    maker.fPaint = &local;
+    bool result = INHERITED::draw(maker);
+    maker.fPaint = save;
+    maker.fCanvas->restore();
+    return result;
+}
+
+
diff --git a/legacy/src/animator/SkDrawGroup.h b/legacy/src/animator/SkDrawGroup.h
new file mode 100644
index 0000000..8124650
--- /dev/null
+++ b/legacy/src/animator/SkDrawGroup.h
@@ -0,0 +1,72 @@
+
+/*
+ * 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 SkDrawGroup_DEFINED
+#define SkDrawGroup_DEFINED
+
+#include "SkDrawable.h"
+#include "SkIntArray.h"
+#include "SkMemberInfo.h"
+
+class SkGroup : public SkDrawable { //interface for schema element <g>
+public:
+    DECLARE_MEMBER_INFO(Group);
+    SkGroup();
+    virtual ~SkGroup();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool contains(SkDisplayable* );
+    SkGroup* copy();
+    SkBool copySet(int index);
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state );
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+    virtual void dumpDrawables(SkAnimateMaker* );
+    virtual void dumpEvents();
+#endif
+    int findGroup(SkDrawable* drawable,  SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList);
+    virtual bool enable(SkAnimateMaker& );
+    SkTDDrawableArray* getChildren() { return &fChildren; }
+    SkGroup* getOriginal() { return fOriginal; }
+    virtual bool hasEnable() const;
+    virtual void initialize();
+    SkBool isACopy() { return fOriginal != NULL; }
+    void markCopyClear(int index);
+    void markCopySet(int index);
+    void markCopySize(int index);
+    bool markedForDelete(int index) const { return (fCopies[index >> 5] & 1 << (index & 0x1f)) == 0; }
+    void reset();
+    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* );
+    virtual void setSteps(int steps);
+#ifdef SK_DEBUG
+    virtual void validate();
+#endif
+protected:
+    bool ifCondition(SkAnimateMaker& maker, SkDrawable* drawable,
+        SkString& conditionString);
+    SkString condition;
+    SkString enableCondition;
+    SkTDDrawableArray fChildren;
+    SkTDDrawableArray* fParentList;
+    SkTDIntArray fCopies;
+    SkGroup* fOriginal;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+class SkSave: public SkGroup {
+    DECLARE_MEMBER_INFO(Save);
+    virtual bool draw(SkAnimateMaker& );
+private:
+    typedef SkGroup INHERITED;
+};
+
+#endif // SkDrawGroup_DEFINED
diff --git a/legacy/src/animator/SkDrawLine.cpp b/legacy/src/animator/SkDrawLine.cpp
new file mode 100644
index 0000000..fe93c4e
--- /dev/null
+++ b/legacy/src/animator/SkDrawLine.cpp
@@ -0,0 +1,35 @@
+
+/*
+ * 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 "SkDrawLine.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkLine::fInfo[] = {
+    SK_MEMBER(x1, Float),
+    SK_MEMBER(x2, Float),
+    SK_MEMBER(y1, Float),
+    SK_MEMBER(y2, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkLine);
+
+SkLine::SkLine() : x1(0), x2(0), y1(0), y2(0) { 
+}
+
+bool SkLine::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawLine(x1, y1, x2, y2, *maker.fPaint);
+    return false;
+}
diff --git a/legacy/src/animator/SkDrawLine.h b/legacy/src/animator/SkDrawLine.h
new file mode 100644
index 0000000..e90c997
--- /dev/null
+++ b/legacy/src/animator/SkDrawLine.h
@@ -0,0 +1,29 @@
+
+/*
+ * 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 SkDrawLine_DEFINED
+#define SkDrawLine_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+
+class SkLine : public SkBoundable {
+    DECLARE_MEMBER_INFO(Line);
+    SkLine();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkScalar x1;
+    SkScalar x2;
+    SkScalar y1;
+    SkScalar y2;
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkDrawLine_DEFINED
+
diff --git a/legacy/src/animator/SkDrawMatrix.cpp b/legacy/src/animator/SkDrawMatrix.cpp
new file mode 100644
index 0000000..96e8292
--- /dev/null
+++ b/legacy/src/animator/SkDrawMatrix.cpp
@@ -0,0 +1,269 @@
+
+/*
+ * 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 "SkDrawMatrix.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkParse.h"
+#include "SkMatrixParts.h"
+#include "SkScript.h"
+#include "SkTypedArray.h"
+
+enum SkDrawMatrix_Properties {
+    SK_PROPERTY(perspectX),
+    SK_PROPERTY(perspectY),
+    SK_PROPERTY(rotate),
+    SK_PROPERTY(scale),
+    SK_PROPERTY(scaleX),
+    SK_PROPERTY(scaleY),
+    SK_PROPERTY(skewX),
+    SK_PROPERTY(skewY),
+    SK_PROPERTY(translate),
+    SK_PROPERTY(translateX),
+    SK_PROPERTY(translateY)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawMatrix::fInfo[] = {
+    SK_MEMBER_ARRAY(matrix, Float),
+    SK_MEMBER_PROPERTY(perspectX, Float),
+    SK_MEMBER_PROPERTY(perspectY, Float),
+    SK_MEMBER_PROPERTY(rotate, Float),
+    SK_MEMBER_PROPERTY(scale, Float),
+    SK_MEMBER_PROPERTY(scaleX, Float),
+    SK_MEMBER_PROPERTY(scaleY, Float),
+    SK_MEMBER_PROPERTY(skewX, Float),
+    SK_MEMBER_PROPERTY(skewY, Float),
+    SK_MEMBER_PROPERTY(translate, Point),
+    SK_MEMBER_PROPERTY(translateX, Float),
+    SK_MEMBER_PROPERTY(translateY, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawMatrix);
+
+SkDrawMatrix::SkDrawMatrix() : fChildHasID(false), fDirty(false) { 
+    fConcat.reset();
+    fMatrix.reset(); 
+}
+
+SkDrawMatrix::~SkDrawMatrix() {
+    for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++)
+        delete *part;
+}
+
+bool SkDrawMatrix::add(SkAnimateMaker& maker, SkDisplayable* child) {
+    SkASSERT(child && child->isMatrixPart());
+    SkMatrixPart* part = (SkMatrixPart*) child;
+    *fParts.append() = part;
+    if (part->add())
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToMatrix); 
+    return true;
+}
+
+bool SkDrawMatrix::childrenNeedDisposing() const { 
+    return false;
+}
+
+SkDisplayable* SkDrawMatrix::deepCopy(SkAnimateMaker* maker) {
+    SkDrawMatrix* copy = (SkDrawMatrix*)
+        SkDisplayType::CreateInstance(maker, SkType_Matrix);
+    SkASSERT(fParts.count() == 0);
+    copy->fMatrix = fMatrix;
+    copy->fConcat = fConcat;
+    return copy;
+}
+
+void SkDrawMatrix::dirty() { 
+    fDirty = true; 
+}
+
+bool SkDrawMatrix::draw(SkAnimateMaker& maker) {
+    SkMatrix& concat = getMatrix();
+    maker.fCanvas->concat(concat);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawMatrix::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (fMatrix.isIdentity()) {
+        SkDebugf("matrix=\"identity\"/>\n");
+        return;
+    }
+    SkScalar result;
+    result = fMatrix[SkMatrix::kMScaleX];
+    if (result != SK_Scalar1)
+        SkDebugf("sx=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getScaleY();
+    if (result != SK_Scalar1)
+        SkDebugf("sy=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getSkewX();
+    if (result)
+        SkDebugf("skew-x=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getSkewY();
+    if (result)
+        SkDebugf("skew-y=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getTranslateX();
+    if (result)
+        SkDebugf("tx=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getTranslateY();
+    if (result)
+        SkDebugf("ty=\"%g\" ", SkScalarToFloat(result));
+    result = SkPerspToScalar(fMatrix.getPerspX());
+    if (result)
+        SkDebugf("perspect-x=\"%g\" ", SkScalarToFloat(result));
+    result = SkPerspToScalar(fMatrix.getPerspY());
+    if (result)
+        SkDebugf("perspect-y=\"%g\" ", SkScalarToFloat(result));
+    SkDebugf("/>\n");
+}
+#endif
+
+SkMatrix& SkDrawMatrix::getMatrix() {
+    if (fDirty == false)
+        return fConcat;
+    fMatrix.reset();
+    for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++) {
+        (*part)->add();
+        fConcat = fMatrix;
+    }
+    fDirty = false;
+    return fConcat;
+}
+
+bool SkDrawMatrix::getProperty(int index, SkScriptValue* value) const {
+    value->fType = SkType_Float;
+    SkScalar result;
+    switch (index) {
+        case SK_PROPERTY(perspectX):
+            result = fMatrix.getPerspX();
+            break;
+        case SK_PROPERTY(perspectY):
+            result = fMatrix.getPerspY();
+            break;
+        case SK_PROPERTY(scaleX):
+            result = fMatrix.getScaleX();
+            break;
+        case SK_PROPERTY(scaleY):
+            result = fMatrix.getScaleY();
+            break;
+        case SK_PROPERTY(skewX):
+            result = fMatrix.getSkewX();
+            break;
+        case SK_PROPERTY(skewY):
+            result = fMatrix.getSkewY();
+            break;
+        case SK_PROPERTY(translateX):
+            result = fMatrix.getTranslateX();
+            break;
+        case SK_PROPERTY(translateY):
+            result = fMatrix.getTranslateY();
+            break;
+        default:
+//          SkASSERT(0);
+            return false;
+    }
+    value->fOperand.fScalar = result;
+    return true;
+}
+
+void SkDrawMatrix::initialize() {
+    fConcat = fMatrix;
+}
+
+void SkDrawMatrix::onEndElement(SkAnimateMaker& ) {
+    if (matrix.count() > 0) {
+        SkScalar* vals = matrix.begin();
+        fMatrix.setScaleX(vals[0]);
+        fMatrix.setSkewX(vals[1]);
+        fMatrix.setTranslateX(vals[2]);
+        fMatrix.setSkewY(vals[3]);
+        fMatrix.setScaleY(vals[4]);
+        fMatrix.setTranslateY(vals[5]);
+        fMatrix.setPerspX(SkScalarToPersp(vals[6]));
+        fMatrix.setPerspY(SkScalarToPersp(vals[7]));
+//      fMatrix.setPerspW(SkScalarToPersp(vals[8]));
+        goto setConcat;
+    }
+    if (fChildHasID == false) {
+        {
+            for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++)
+                delete *part;
+        }
+        fParts.reset();
+setConcat:
+        fConcat = fMatrix;
+        fDirty = false;
+    }
+}
+
+void SkDrawMatrix::setChildHasID() { 
+    fChildHasID = true; 
+}
+
+bool SkDrawMatrix::setProperty(int index, SkScriptValue& scriptValue) {
+    SkScalar number = scriptValue.fOperand.fScalar;
+    switch (index) {
+        case SK_PROPERTY(translate):
+    //      SkScalar xy[2];
+            SkASSERT(scriptValue.fType == SkType_Array);
+            SkASSERT(scriptValue.fOperand.fArray->getType() == SkType_Float);
+            SkASSERT(scriptValue.fOperand.fArray->count() == 2);
+    //      SkParse::FindScalars(scriptValue.fOperand.fString->c_str(), xy, 2);
+            fMatrix.setTranslateX((*scriptValue.fOperand.fArray)[0].fScalar);
+            fMatrix.setTranslateY((*scriptValue.fOperand.fArray)[1].fScalar);
+            return true;
+        case SK_PROPERTY(perspectX):
+            fMatrix.setPerspX(SkScalarToPersp((number)));
+            break;
+        case SK_PROPERTY(perspectY):
+            fMatrix.setPerspY(SkScalarToPersp((number)));
+            break;
+        case SK_PROPERTY(rotate): {
+            SkMatrix temp;
+            temp.setRotate(number, 0, 0);
+            fMatrix.setScaleX(temp.getScaleX());
+            fMatrix.setScaleY(temp.getScaleY());
+            fMatrix.setSkewX(temp.getSkewX());
+            fMatrix.setSkewY(temp.getSkewY());
+            } break;
+        case SK_PROPERTY(scale):
+            fMatrix.setScaleX(number);
+            fMatrix.setScaleY(number);
+            break;
+        case SK_PROPERTY(scaleX):
+            fMatrix.setScaleX(number);
+            break;
+        case SK_PROPERTY(scaleY):
+            fMatrix.setScaleY(number);
+            break;
+        case SK_PROPERTY(skewX):
+            fMatrix.setSkewX(number);
+            break;
+        case SK_PROPERTY(skewY):
+            fMatrix.setSkewY(number);
+            break;
+        case SK_PROPERTY(translateX):
+            fMatrix.setTranslateX(number);
+            break;
+        case SK_PROPERTY(translateY):
+            fMatrix.setTranslateY(number);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    fConcat = fMatrix;
+    return true;
+}
+
diff --git a/legacy/src/animator/SkDrawMatrix.h b/legacy/src/animator/SkDrawMatrix.h
new file mode 100644
index 0000000..e303424
--- /dev/null
+++ b/legacy/src/animator/SkDrawMatrix.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 SkDrawMatrix_DEFINED
+#define SkDrawMatrix_DEFINED
+
+#include "SkDrawable.h"
+#include "SkMatrix.h"
+#include "SkMemberInfo.h"
+#include "SkIntArray.h"
+
+class SkMatrixPart;
+
+class SkDrawMatrix : public SkDrawable {
+    DECLARE_DRAW_MEMBER_INFO(Matrix);
+    SkDrawMatrix();
+    virtual ~SkDrawMatrix();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool childrenNeedDisposing() const;
+    virtual void dirty();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkMatrix& getMatrix();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual void initialize();
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void setChildHasID();
+    virtual bool setProperty(int index, SkScriptValue& );
+
+    void concat(SkMatrix& inMatrix) {
+        fConcat.preConcat(inMatrix);
+    }
+
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+
+
+    void rotate(SkScalar degrees, SkPoint& center) {
+        fMatrix.preRotate(degrees, center.fX, center.fY);
+    }
+
+    void set(SkMatrix& src) {
+        fMatrix.preConcat(src);
+    }
+
+    void scale(SkScalar scaleX, SkScalar scaleY, SkPoint& center) {
+        fMatrix.preScale(scaleX, scaleY, center.fX, center.fY);
+    }
+
+    void skew(SkScalar skewX, SkScalar skewY, SkPoint& center) {
+        fMatrix.preSkew(skewX, skewY, center.fX, center.fY);
+    }
+
+    void translate(SkScalar x, SkScalar y) {
+        fMatrix.preTranslate(x, y);
+    }
+private:
+    SkTDScalarArray matrix;
+    SkMatrix fConcat;
+    SkMatrix fMatrix;
+    SkTDMatrixPartArray fParts;
+    SkBool8 fChildHasID;
+    SkBool8 fDirty;
+    typedef SkDrawable INHERITED;
+};
+
+#endif // SkDrawMatrix_DEFINED
diff --git a/legacy/src/animator/SkDrawOval.cpp b/legacy/src/animator/SkDrawOval.cpp
new file mode 100644
index 0000000..ab0fc81
--- /dev/null
+++ b/legacy/src/animator/SkDrawOval.cpp
@@ -0,0 +1,29 @@
+
+/*
+ * 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 "SkDrawOval.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkOval::fInfo[] = {
+    SK_MEMBER_INHERITED,
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkOval);
+
+bool SkOval::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawOval(fRect, *maker.fPaint);
+    return false;
+}
+
diff --git a/legacy/src/animator/SkDrawOval.h b/legacy/src/animator/SkDrawOval.h
new file mode 100644
index 0000000..afdc252
--- /dev/null
+++ b/legacy/src/animator/SkDrawOval.h
@@ -0,0 +1,23 @@
+
+/*
+ * 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 SkDrawOval_DEFINED
+#define SkDrawOval_DEFINED
+
+#include "SkDrawRectangle.h"
+
+class SkOval : public SkDrawRect {
+    DECLARE_MEMBER_INFO(Oval);
+    virtual bool draw(SkAnimateMaker& );
+private:
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDrawOval_DEFINED
+
diff --git a/legacy/src/animator/SkDrawPaint.cpp b/legacy/src/animator/SkDrawPaint.cpp
new file mode 100644
index 0000000..4d1bd8b
--- /dev/null
+++ b/legacy/src/animator/SkDrawPaint.cpp
@@ -0,0 +1,269 @@
+
+/*
+ * 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 "SkDrawPaint.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawColor.h"
+#include "SkDrawShader.h"
+#include "SkMaskFilter.h"
+#include "SkPaintParts.h"
+#include "SkPathEffect.h"
+
+enum SkPaint_Functions {
+    SK_FUNCTION(measureText)
+};
+
+enum SkPaint_Properties {
+    SK_PROPERTY(ascent),
+    SK_PROPERTY(descent)
+};
+
+// !!! in the future, this could be compiled by build-condensed-info into an array of parameters
+// with a lookup table to find the first parameter -- for now, it is iteratively searched through
+const SkFunctionParamType SkDrawPaint::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_String,
+    (SkFunctionParamType) 0 // terminator for parameter list (there may be multiple parameter lists)
+};
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawPaint::fInfo[] = {
+    SK_MEMBER(antiAlias, Boolean),
+    SK_MEMBER_PROPERTY(ascent, Float),
+    SK_MEMBER(color, Color),
+    SK_MEMBER_PROPERTY(descent, Float),
+    SK_MEMBER(fakeBold, Boolean),
+    SK_MEMBER(filterBitmap, Boolean),
+    SK_MEMBER(linearText, Boolean),
+    SK_MEMBER(maskFilter, MaskFilter),
+    SK_MEMBER_FUNCTION(measureText, Float),
+    SK_MEMBER(pathEffect, PathEffect),
+    SK_MEMBER(shader, Shader),
+    SK_MEMBER(strikeThru, Boolean),
+    SK_MEMBER(stroke, Boolean),
+    SK_MEMBER(strokeCap, Cap),
+    SK_MEMBER(strokeJoin, Join),
+    SK_MEMBER(strokeMiter, Float),
+    SK_MEMBER(strokeWidth, Float),
+    SK_MEMBER(style, Style),
+    SK_MEMBER(textAlign, Align),
+    SK_MEMBER(textScaleX, Float),
+    SK_MEMBER(textSize, Float),
+    SK_MEMBER(textSkewX, Float),
+    SK_MEMBER(typeface, Typeface),
+    SK_MEMBER(underline, Boolean),
+    SK_MEMBER(xfermode, Xfermode)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawPaint);
+
+SkDrawPaint::SkDrawPaint() : antiAlias(-1), color(NULL), fakeBold(-1), filterBitmap(-1),
+    linearText(-1), maskFilter((SkDrawMaskFilter*) -1), pathEffect((SkDrawPathEffect*) -1),
+    shader((SkDrawShader*) -1), strikeThru(-1), stroke(-1),
+    strokeCap((SkPaint::Cap) -1), strokeJoin((SkPaint::Join) -1), strokeMiter(SK_ScalarNaN),
+    strokeWidth(SK_ScalarNaN), style((SkPaint::Style) -1),
+    textAlign((SkPaint::Align) -1), textScaleX(SK_ScalarNaN), textSize(SK_ScalarNaN),
+    textSkewX(SK_ScalarNaN), typeface((SkDrawTypeface*) -1),
+    underline(-1), xfermode((SkXfermode::Mode) -1), fOwnsColor(false), fOwnsMaskFilter(false),
+    fOwnsPathEffect(false), fOwnsShader(false), fOwnsTypeface(false) {
+}
+
+SkDrawPaint::~SkDrawPaint() {
+    if (fOwnsColor)
+        delete color;
+    if (fOwnsMaskFilter)
+        delete maskFilter;
+    if (fOwnsPathEffect)
+        delete pathEffect;
+    if (fOwnsShader)
+        delete shader;
+    if (fOwnsTypeface)
+        delete typeface;
+}
+
+bool SkDrawPaint::add(SkAnimateMaker* maker, SkDisplayable* child) {
+    SkASSERT(child && child->isPaintPart());
+    SkPaintPart* part = (SkPaintPart*) child;
+    if (part->add() && maker)
+        maker->setErrorCode(SkDisplayXMLParserError::kErrorAddingToPaint);
+    return true;
+}
+
+SkDisplayable* SkDrawPaint::deepCopy(SkAnimateMaker* maker) {
+    SkDrawColor* tempColor = color;
+    color = NULL;
+    SkDrawPaint* copy = (SkDrawPaint*) INHERITED::deepCopy(maker);
+    color = tempColor;
+    tempColor = (SkDrawColor*) color->deepCopy(maker);
+    tempColor->setParent(copy);
+    tempColor->add();
+    copy->fOwnsColor = true;
+    return copy;
+}
+
+bool SkDrawPaint::draw(SkAnimateMaker& maker) {
+    SkPaint* paint = maker.fPaint;
+    setupPaint(paint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawPaint::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    bool closedYet = false;
+    SkDisplayList::fIndent +=4;
+    //should i say if (maskFilter && ...?
+    if (maskFilter != (SkDrawMaskFilter*)-1) {
+        SkDebugf(">\n");
+        maskFilter->dump(maker);
+        closedYet = true;
+    }
+    if (pathEffect != (SkDrawPathEffect*) -1) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        pathEffect->dump(maker);
+    }
+    if (fOwnsTypeface) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        typeface->dump(maker);
+    }
+    SkDisplayList::fIndent -= 4;
+    dumpChildren(maker, closedYet);
+}
+#endif
+
+void SkDrawPaint::executeFunction(SkDisplayable* target, int index,
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* scriptValue) {
+        if (scriptValue == NULL)
+            return;
+    SkASSERT(target == this);
+    switch (index) {
+        case SK_FUNCTION(measureText): {
+            SkASSERT(parameters.count() == 1);
+            SkASSERT(type == SkType_Float);
+            SkPaint paint;
+            setupPaint(&paint);
+            scriptValue->fType = SkType_Float;
+            SkASSERT(parameters[0].fType == SkType_String);
+            scriptValue->fOperand.fScalar = paint.measureText(parameters[0].fOperand.fString->c_str(),
+                parameters[0].fOperand.fString->size());
+//          SkDebugf("measureText: %s = %g\n", parameters[0].fOperand.fString->c_str(),
+//              scriptValue->fOperand.fScalar / 65536.0f);
+            } break;
+        default:
+            SkASSERT(0);
+    }
+}
+
+const SkFunctionParamType* SkDrawPaint::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+bool SkDrawPaint::getProperty(int index, SkScriptValue* value) const {
+    SkPaint::FontMetrics    metrics;
+    SkPaint paint;
+    setupPaint(&paint);
+    paint.getFontMetrics(&metrics);
+    switch (index) {
+        case SK_PROPERTY(ascent):
+            value->fOperand.fScalar = metrics.fAscent;
+            break;
+        case SK_PROPERTY(descent):
+            value->fOperand.fScalar = metrics.fDescent;
+            break;
+        // should consider returning fLeading as well (or roll it into ascent/descent somehow
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fType = SkType_Float;
+    return true;
+}
+
+bool SkDrawPaint::resolveIDs(SkAnimateMaker& maker, SkDisplayable* origDisp, SkApply* ) {
+    SkASSERT(origDisp->isPaint());
+    SkDrawPaint* original = (SkDrawPaint*) origDisp;
+    if (fOwnsColor && maker.resolveID(color, original->color) == false)
+        return true;
+    if (fOwnsMaskFilter && maker.resolveID(maskFilter, original->maskFilter) == false)
+        return true;
+    if (fOwnsPathEffect && maker.resolveID(pathEffect, original->pathEffect) == false)
+        return true;
+    if (fOwnsShader && maker.resolveID(shader, original->shader) == false)
+        return true;
+    if (fOwnsTypeface && maker.resolveID(typeface, original->typeface) == false)
+        return true;
+    return false; // succeeded
+}
+
+void SkDrawPaint::setupPaint(SkPaint* paint) const {
+    if (antiAlias != -1)
+        paint->setAntiAlias(SkToBool(antiAlias));
+    if (color != NULL)
+        paint->setColor(color->getColor());
+    if (fakeBold != -1)
+        paint->setFakeBoldText(SkToBool(fakeBold));
+    if (filterBitmap != -1)
+        paint->setFilterBitmap(SkToBool(filterBitmap));
+    //  stroke is legacy; style setting if present overrides stroke
+    if (stroke != -1)
+        paint->setStyle(SkToBool(stroke) ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+    if (style != (SkPaint::Style) -1)
+        paint->setStyle((SkPaint::Style) style);
+    if (linearText != -1)
+        paint->setLinearText(SkToBool(linearText));
+    if (maskFilter == NULL)
+        paint->setMaskFilter(NULL);
+    else if (maskFilter != (SkDrawMaskFilter*) -1)
+        SkSafeUnref(paint->setMaskFilter(maskFilter->getMaskFilter()));
+    if (pathEffect == NULL)
+        paint->setPathEffect(NULL);
+    else if (pathEffect != (SkDrawPathEffect*) -1)
+        SkSafeUnref(paint->setPathEffect(pathEffect->getPathEffect()));
+    if (shader == NULL)
+        paint->setShader(NULL);
+    else if (shader != (SkDrawShader*) -1)
+        SkSafeUnref(paint->setShader(shader->getShader()));
+    if (strikeThru != -1)
+        paint->setStrikeThruText(SkToBool(strikeThru));
+    if (strokeCap != (SkPaint::Cap) -1)
+        paint->setStrokeCap((SkPaint::Cap) strokeCap);
+    if (strokeJoin != (SkPaint::Join) -1)
+        paint->setStrokeJoin((SkPaint::Join) strokeJoin);
+    if (SkScalarIsNaN(strokeMiter) == false)
+        paint->setStrokeMiter(strokeMiter);
+    if (SkScalarIsNaN(strokeWidth) == false)
+        paint->setStrokeWidth(strokeWidth);
+    if (textAlign != (SkPaint::Align) -1)
+        paint->setTextAlign((SkPaint::Align) textAlign);
+    if (SkScalarIsNaN(textScaleX) == false)
+        paint->setTextScaleX(textScaleX);
+    if (SkScalarIsNaN(textSize) == false)
+        paint->setTextSize(textSize);
+    if (SkScalarIsNaN(textSkewX) == false)
+        paint->setTextSkewX(textSkewX);
+    if (typeface == NULL)
+        paint->setTypeface(NULL);
+    else if (typeface != (SkDrawTypeface*) -1)
+        SkSafeUnref(paint->setTypeface(typeface->getTypeface()));
+    if (underline != -1)
+        paint->setUnderlineText(SkToBool(underline));
+    if (xfermode != (SkXfermode::Mode) -1)
+        paint->setXfermodeMode((SkXfermode::Mode) xfermode);
+}
diff --git a/legacy/src/animator/SkDrawPaint.h b/legacy/src/animator/SkDrawPaint.h
new file mode 100644
index 0000000..a2a893e
--- /dev/null
+++ b/legacy/src/animator/SkDrawPaint.h
@@ -0,0 +1,80 @@
+
+/*
+ * 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 SkDrawPaint_DEFINED
+#define SkDrawPaint_DEFINED
+
+#include "SkDrawable.h"
+#include "SkIntArray.h"
+#include "SkMemberInfo.h"
+#include "SkPaint.h"
+#include "SkXfermode.h"
+
+class SkDrawMaskFilter;
+class SkDrawPathEffect;
+class SkDrawShader;
+class SkTransferMode;
+class SkDrawTypeface;
+
+class SkDrawPaint : public SkDrawable {
+    DECLARE_DRAW_MEMBER_INFO(Paint);
+    SkDrawPaint();
+    virtual ~SkDrawPaint();
+    virtual bool add(SkAnimateMaker* , SkDisplayable* child);
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply);
+protected:
+    static const SkFunctionParamType fFunctionParameters[];
+    void setupPaint(SkPaint* paint) const;
+public:
+    SkBool antiAlias;
+    SkDrawColor* color;
+    SkBool fakeBold;
+    SkBool filterBitmap;
+    SkBool linearText;
+    SkDrawMaskFilter* maskFilter;
+    SkDrawPathEffect* pathEffect;
+    SkDrawShader* shader;
+    SkBool strikeThru;
+    SkBool stroke;
+    int /*SkPaint::Cap*/ strokeCap;
+    int /*SkPaint::Join */ strokeJoin;
+    SkScalar strokeMiter;
+    SkScalar strokeWidth;
+    int /* SkPaint::Style */ style;
+    int /* SkPaint::Align */ textAlign;
+    SkScalar textScaleX;
+    SkScalar textSize;
+    SkScalar textSkewX;
+    SkDrawTypeface* typeface;
+    SkBool underline;
+    int /*SkXfermode::Modes*/ xfermode;
+    SkBool8 fOwnsColor;
+    SkBool8 fOwnsMaskFilter;
+    SkBool8 fOwnsPathEffect;
+    SkBool8 fOwnsShader;
+    SkBool8 fOwnsTransferMode;
+    SkBool8 fOwnsTypeface;
+private:
+    typedef SkDrawable INHERITED;
+    friend class SkTextToPath;
+    friend class SkSaveLayer;
+};
+
+#endif // SkDrawPaint_DEFINED
+
diff --git a/legacy/src/animator/SkDrawPath.cpp b/legacy/src/animator/SkDrawPath.cpp
new file mode 100644
index 0000000..10e9203
--- /dev/null
+++ b/legacy/src/animator/SkDrawPath.cpp
@@ -0,0 +1,221 @@
+
+/*
+ * 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 "SkDrawPath.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkMath.h"
+#include "SkMatrixParts.h"
+#include "SkPaint.h"
+#include "SkPathParts.h"
+
+enum SkPath_Properties {
+    SK_PROPERTY(fillType),
+    SK_PROPERTY(length)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawPath::fInfo[] = {
+    SK_MEMBER(d, String),
+    SK_MEMBER_PROPERTY(fillType, FillType),
+    SK_MEMBER_PROPERTY(length, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawPath);
+
+SkDrawPath::SkDrawPath()
+{
+    fParent = NULL;
+    fLength = SK_ScalarNaN;
+    fChildHasID = false;
+    fDirty = false;
+}
+
+SkDrawPath::~SkDrawPath() {
+    for (SkPathPart** part = fParts.begin(); part < fParts.end();  part++)
+        delete *part;
+}
+
+bool SkDrawPath::add(SkAnimateMaker& maker, SkDisplayable* child) {
+    SkASSERT(child && child->isPathPart());
+    SkPathPart* part = (SkPathPart*) child;
+    *fParts.append() = part;
+    if (part->add())
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPath); 
+    fDirty = false;
+    return true;
+}
+
+bool SkDrawPath::childrenNeedDisposing() const { 
+    return false; 
+}
+
+void SkDrawPath::dirty() { 
+    fDirty = true; 
+    fLength = SK_ScalarNaN;
+    if (fParent)
+        fParent->dirty();
+}
+
+bool SkDrawPath::draw(SkAnimateMaker& maker) {
+    SkPath& path = getPath();
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawPath(path, *maker.fPaint);
+    return false;
+}
+
+SkDisplayable* SkDrawPath::getParent() const {
+    return fParent;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawPath::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    for(SkPathPart** part = fParts.begin(); part < fParts.end(); part++) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        (*part)->dump(maker);
+    }
+    SkDisplayList::fIndent -= 4;
+    if (closedYet)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+}
+#endif
+
+SkPath& SkDrawPath::getPath() {
+    if (fDirty == false)
+        return fPath;
+    if (d.size() > 0)
+    {
+        parseSVG();
+        d.reset();
+    }
+    else
+    {
+        fPath.reset();
+        for (SkPathPart** part = fParts.begin(); part < fParts.end();  part++)
+            (*part)->add();
+    }
+    fDirty = false;
+    return fPath;
+}
+    
+void SkDrawPath::onEndElement(SkAnimateMaker& ) {
+    if (d.size() > 0) {
+        parseSVG();
+        d.reset();
+        fDirty = false;
+        return;
+    }
+    if (fChildHasID == false) {
+        for (SkPathPart** part = fParts.begin(); part < fParts.end();  part++)
+            delete *part;
+        fParts.reset();
+        fDirty = false;
+    }
+}
+
+bool SkDrawPath::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(length):
+            if (SkScalarIsNaN(fLength)) {
+                const SkPath& path = ((SkDrawPath*) this)->getPath();
+                SkPathMeasure pathMeasure(path, false);
+                fLength = pathMeasure.getLength();
+            }
+            value->fType = SkType_Float;
+            value->fOperand.fScalar = fLength;
+            break;
+        case SK_PROPERTY(fillType):
+            value->fType = SkType_FillType;
+            value->fOperand.fS32 = (int) fPath.getFillType();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+void SkDrawPath::setChildHasID() { 
+    fChildHasID = true; 
+}
+
+bool SkDrawPath::setParent(SkDisplayable* parent) {
+    fParent = parent;
+    return false;
+}
+
+bool SkDrawPath::setProperty(int index, SkScriptValue& value)
+{
+    switch (index) {
+        case SK_PROPERTY(fillType):
+            SkASSERT(value.fType == SkType_FillType);
+            SkASSERT(value.fOperand.fS32 >= SkPath::kWinding_FillType &&
+                value.fOperand.fS32 <= SkPath::kEvenOdd_FillType);
+            fPath.setFillType((SkPath::FillType) value.fOperand.fS32);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPolyline::fInfo[] = {
+    SK_MEMBER_ARRAY(points, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPolyline);
+
+bool SkPolyline::add(SkAnimateMaker& , SkDisplayable*) const {
+    return false; 
+}
+
+void SkPolyline::onEndElement(SkAnimateMaker& maker) {
+    INHERITED::onEndElement(maker);
+    if (points.count() <= 0)
+        return;
+    fPath.reset();
+    fPath.moveTo(points[0], points[1]);
+    int count = points.count();
+    for (int index = 2; index < count; index += 2)
+        fPath.lineTo(points[index], points[index+1]);
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPolygon::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPolygon);
+
+void SkPolygon::onEndElement(SkAnimateMaker& maker) {
+    INHERITED::onEndElement(maker);
+    fPath.close();
+}
+
diff --git a/legacy/src/animator/SkDrawPath.h b/legacy/src/animator/SkDrawPath.h
new file mode 100644
index 0000000..5c26312
--- /dev/null
+++ b/legacy/src/animator/SkDrawPath.h
@@ -0,0 +1,69 @@
+
+/*
+ * 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 SkDrawPath_DEFINED
+#define SkDrawPath_DEFINED
+
+#include "SkBoundable.h"
+#include "SkIntArray.h"
+#include "SkMemberInfo.h"
+#include "SkPath.h"
+
+class SkDrawPath : public SkBoundable {
+    DECLARE_DRAW_MEMBER_INFO(Path);
+    SkDrawPath();
+    virtual ~SkDrawPath();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    bool childHasID() { return SkToBool(fChildHasID); }
+    virtual bool childrenNeedDisposing() const;
+    virtual void dirty();
+    virtual bool draw(SkAnimateMaker& );
+    virtual SkDisplayable* getParent() const;
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkPath& getPath();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setProperty(int index, SkScriptValue& value);
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void setChildHasID();
+    virtual bool setParent(SkDisplayable* parent);
+    virtual bool isPath() const { return true; }
+public:
+    SkPath fPath;
+protected:
+    void parseSVG();
+    SkString d;
+    SkTDPathPartArray fParts;
+    mutable SkScalar fLength;
+    SkDisplayable* fParent; // SkPolyToPoly or SkFromPath, for instance
+    SkBool8 fChildHasID;
+    SkBool8 fDirty;
+private:
+    typedef SkBoundable INHERITED;
+};
+
+class SkPolyline : public SkDrawPath {
+    DECLARE_MEMBER_INFO(Polyline);
+    virtual bool add(SkAnimateMaker& , SkDisplayable*) const;
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkTDScalarArray points;
+private:
+    typedef SkDrawPath INHERITED;
+};
+
+class SkPolygon : public SkPolyline {
+    DECLARE_MEMBER_INFO(Polygon);
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    typedef SkPolyline INHERITED;
+};
+
+#endif // SkDrawPath_DEFINED
diff --git a/legacy/src/animator/SkDrawPoint.cpp b/legacy/src/animator/SkDrawPoint.cpp
new file mode 100644
index 0000000..94fe4d2
--- /dev/null
+++ b/legacy/src/animator/SkDrawPoint.cpp
@@ -0,0 +1,46 @@
+
+/*
+ * 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 "SkDrawPoint.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk_Point::fInfo[] = {
+    SK_MEMBER_ALIAS(x, fPoint.fX, Float),
+    SK_MEMBER_ALIAS(y, fPoint.fY, Float)
+};
+
+#endif
+
+DEFINE_NO_VIRTUALS_GET_MEMBER(Sk_Point);
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawPoint::fInfo[] = {
+    SK_MEMBER_ALIAS(x, fPoint.fX, Float),
+    SK_MEMBER_ALIAS(y, fPoint.fY, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawPoint);
+
+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/legacy/src/animator/SkDrawPoint.h b/legacy/src/animator/SkDrawPoint.h
new file mode 100644
index 0000000..0ecf447
--- /dev/null
+++ b/legacy/src/animator/SkDrawPoint.h
@@ -0,0 +1,33 @@
+
+/*
+ * 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 SkDrawPoint_DEFINED
+#define SkDrawPoint_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+#include "SkPoint.h"
+
+struct Sk_Point {
+    DECLARE_NO_VIRTUALS_MEMBER_INFO(_Point);
+    Sk_Point();
+private:
+    SkPoint fPoint;
+};
+
+class SkDrawPoint : public SkDisplayable {
+    DECLARE_MEMBER_INFO(DrawPoint);
+    SkDrawPoint();
+    virtual void getBounds(SkRect*  );
+private:
+    SkPoint fPoint;
+    typedef SkDisplayable INHERITED;
+};
+
+#endif // SkDrawPoint_DEFINED
diff --git a/legacy/src/animator/SkDrawRectangle.cpp b/legacy/src/animator/SkDrawRectangle.cpp
new file mode 100644
index 0000000..19edf50
--- /dev/null
+++ b/legacy/src/animator/SkDrawRectangle.cpp
@@ -0,0 +1,145 @@
+
+/*
+ * 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 "SkDrawRectangle.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkMatrixParts.h"
+#include "SkPaint.h"
+#include "SkScript.h"
+
+enum SkRectangle_Properties {
+    SK_PROPERTY(height),
+    SK_PROPERTY(needsRedraw),
+    SK_PROPERTY(width)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawRect::fInfo[] = {
+    SK_MEMBER_ALIAS(bottom, fRect.fBottom, Float),
+    SK_MEMBER_PROPERTY(height, Float),
+    SK_MEMBER_ALIAS(left, fRect.fLeft, Float),
+    SK_MEMBER_PROPERTY(needsRedraw, Boolean),
+    SK_MEMBER_ALIAS(right, fRect.fRight, Float),
+    SK_MEMBER_ALIAS(top, fRect.fTop, Float),
+    SK_MEMBER_PROPERTY(width, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawRect);
+
+SkDrawRect::SkDrawRect() : fParent(NULL) { 
+    fRect.setEmpty(); 
+}
+
+void SkDrawRect::dirty() {
+    if (fParent)
+        fParent->dirty();
+}
+
+bool SkDrawRect::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawRect(fRect, *maker.fPaint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawRect::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("left=\"%g\" top=\"%g\" right=\"%g\" bottom=\"%g\" />\n",
+        SkScalarToFloat(fRect.fLeft), SkScalarToFloat(fRect.fTop), SkScalarToFloat(fRect.fRight),
+        SkScalarToFloat(fRect.fBottom));
+}
+#endif
+
+SkDisplayable* SkDrawRect::getParent() const {
+    return fParent;
+}
+
+bool SkDrawRect::getProperty(int index, SkScriptValue* value) const {
+    SkScalar result;
+    switch (index) {
+        case SK_PROPERTY(height):
+            result = fRect.height();
+            break;
+        case SK_PROPERTY(needsRedraw):
+            value->fType = SkType_Boolean;
+            value->fOperand.fS32 = fBounds.isEmpty() == false;
+            return true;
+        case SK_PROPERTY(width):
+            result = fRect.width();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fType = SkType_Float;
+    value->fOperand.fScalar = result;
+    return true;
+}
+
+
+bool SkDrawRect::setParent(SkDisplayable* parent) {
+    fParent = parent;
+    return false;
+}
+
+bool SkDrawRect::setProperty(int index, SkScriptValue& value) {
+    SkScalar scalar = value.fOperand.fScalar;
+    switch (index) {
+        case SK_PROPERTY(height):
+            SkASSERT(value.fType == SkType_Float);
+            fRect.fBottom = scalar + fRect.fTop;
+            return true;
+        case SK_PROPERTY(needsRedraw):
+            return false;
+        case SK_PROPERTY(width):
+            SkASSERT(value.fType == SkType_Float);
+            fRect.fRight = scalar + fRect.fLeft;
+            return true;
+        default:
+            SkASSERT(0);
+    }
+    return false;
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRoundRect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(rx, Float),
+    SK_MEMBER(ry, Float),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRoundRect);
+
+SkRoundRect::SkRoundRect() : rx(0), ry(0) {
+}
+
+bool SkRoundRect::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawRoundRect(fRect, rx, ry, *maker.fPaint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkRoundRect::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("left=\"%g\" top=\"%g\" right=\"%g\" bottom=\"%g\" rx=\"%g\" ry=\"%g\" />\n",
+            SkScalarToFloat(fRect.fLeft), SkScalarToFloat(fRect.fTop), SkScalarToFloat(fRect.fRight),
+            SkScalarToFloat(fRect.fBottom), SkScalarToFloat(rx), SkScalarToFloat(ry));
+}
+#endif
+
+
+
diff --git a/legacy/src/animator/SkDrawRectangle.h b/legacy/src/animator/SkDrawRectangle.h
new file mode 100644
index 0000000..49c9cf4
--- /dev/null
+++ b/legacy/src/animator/SkDrawRectangle.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 SkDrawRectangle_DEFINED
+#define SkDrawRectangle_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+#include "SkRect.h"
+
+class SkRectToRect;
+
+class SkDrawRect : public SkBoundable {
+    DECLARE_DRAW_MEMBER_INFO(Rect);
+    SkDrawRect();
+    virtual void dirty();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual SkDisplayable* getParent() const;
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setParent(SkDisplayable* parent);
+    virtual bool setProperty(int index, SkScriptValue& );
+protected:
+    SkRect fRect;
+    SkDisplayable* fParent;
+private:
+    friend class SkDrawClip;
+    friend class SkRectToRect;
+    friend class SkSaveLayer;
+    typedef SkBoundable INHERITED;
+};
+
+class SkRoundRect : public SkDrawRect {
+    DECLARE_MEMBER_INFO(RoundRect);
+    SkRoundRect();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif    
+protected:
+    SkScalar rx;
+    SkScalar ry;
+private:
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDrawRectangle_DEFINED
+
diff --git a/legacy/src/animator/SkDrawSaveLayer.cpp b/legacy/src/animator/SkDrawSaveLayer.cpp
new file mode 100644
index 0000000..a7592db
--- /dev/null
+++ b/legacy/src/animator/SkDrawSaveLayer.cpp
@@ -0,0 +1,78 @@
+
+/*
+ * 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 "SkDrawSaveLayer.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawPaint.h"
+#include "SkDrawRectangle.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSaveLayer::fInfo[] = {
+    SK_MEMBER(bounds, Rect),
+    SK_MEMBER(paint, Paint)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSaveLayer);
+
+SkSaveLayer::SkSaveLayer() : paint(NULL), bounds(NULL) {
+}
+
+SkSaveLayer::~SkSaveLayer(){
+}
+
+bool SkSaveLayer::draw(SkAnimateMaker& maker)
+{
+    if (!bounds) {
+        return false;
+    }
+    SkPaint* save = maker.fPaint;   
+    //paint is an SkDrawPaint
+    if (paint)
+    {
+        SkPaint realPaint;
+        paint->setupPaint(&realPaint);
+        maker.fCanvas->saveLayer(&bounds->fRect, &realPaint, SkCanvas::kHasAlphaLayer_SaveFlag);
+    }
+    else
+        maker.fCanvas->saveLayer(&bounds->fRect, save, SkCanvas::kHasAlphaLayer_SaveFlag);
+    SkPaint local = SkPaint(*maker.fPaint);
+    maker.fPaint = &local;
+    bool result = INHERITED::draw(maker);
+    maker.fPaint = save;
+    maker.fCanvas->restore();
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkSaveLayer::dump(SkAnimateMaker* maker)
+{
+    dumpBase(maker);
+    //would dump enabled be defined but not debug?
+#ifdef SK_DEBUG
+    if (paint)
+        SkDebugf("paint=\"%s\" ", paint->id);
+    if (bounds)
+        SkDebugf("bounds=\"%s\" ", bounds->id);
+#endif
+    dumpDrawables(maker);
+}
+#endif
+
+void SkSaveLayer::onEndElement(SkAnimateMaker& maker)
+{
+    if (!bounds)
+        maker.setErrorCode(SkDisplayXMLParserError::kSaveLayerNeedsBounds);
+    INHERITED::onEndElement(maker);
+}
+
+
diff --git a/legacy/src/animator/SkDrawSaveLayer.h b/legacy/src/animator/SkDrawSaveLayer.h
new file mode 100644
index 0000000..5c3e068
--- /dev/null
+++ b/legacy/src/animator/SkDrawSaveLayer.h
@@ -0,0 +1,36 @@
+
+/*
+ * 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 SkDrawSaveLayer_DEFINED
+#define SkDrawSaveLayer_DEFINED
+
+#include "SkDrawGroup.h"
+#include "SkMemberInfo.h"
+
+class SkDrawPaint;
+class SkDrawRect;
+
+class SkSaveLayer : public SkGroup {
+    DECLARE_MEMBER_INFO(SaveLayer);
+    SkSaveLayer();
+    virtual ~SkSaveLayer();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkDrawPaint* paint;
+    SkDrawRect* bounds;
+private:
+    typedef SkGroup INHERITED;
+
+};
+
+#endif //SkDrawSaveLayer_DEFINED
diff --git a/legacy/src/animator/SkDrawShader.cpp b/legacy/src/animator/SkDrawShader.cpp
new file mode 100644
index 0000000..5019622
--- /dev/null
+++ b/legacy/src/animator/SkDrawShader.cpp
@@ -0,0 +1,83 @@
+
+/*
+ * 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 "SkDrawShader.h"
+#include "SkDrawBitmap.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawPaint.h"
+#include "SkTemplates.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShader::fInfo[] = {
+    SK_MEMBER(matrix, Matrix),
+    SK_MEMBER(tileMode, TileMode)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShader);
+
+SkDrawShader::SkDrawShader() : matrix(NULL), 
+    tileMode(SkShader::kClamp_TileMode) {
+}
+
+bool SkDrawShader::add() {
+    if (fPaint->shader != (SkDrawShader*) -1)
+        return true;
+    fPaint->shader = this;
+    fPaint->fOwnsShader = true;
+    return false;
+}
+
+void SkDrawShader::addPostlude(SkShader* shader) {
+    if (matrix)
+        shader->setLocalMatrix(matrix->getMatrix());
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawBitmapShader::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(filterBitmap, Boolean),
+    SK_MEMBER(image, BaseBitmap)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawBitmapShader);
+
+SkDrawBitmapShader::SkDrawBitmapShader() : filterBitmap(-1), image(NULL) {}
+
+bool SkDrawBitmapShader::add() {
+    if (fPaint->shader != (SkDrawShader*) -1)
+        return true;
+    fPaint->shader = this;
+    fPaint->fOwnsShader = true;
+    return false;
+}
+
+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, 
+                                                    (SkShader::TileMode) tileMode,
+                                                    (SkShader::TileMode) tileMode);
+    SkAutoTDelete<SkShader> autoDel(shader);
+    addPostlude(shader);
+    (void)autoDel.detach();
+    return shader;
+}
diff --git a/legacy/src/animator/SkDrawShader.h b/legacy/src/animator/SkDrawShader.h
new file mode 100644
index 0000000..b6a487a
--- /dev/null
+++ b/legacy/src/animator/SkDrawShader.h
@@ -0,0 +1,30 @@
+
+/*
+ * 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 SkDrawShader_DEFINED
+#define SkDrawShader_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkShader.h"
+
+class SkBaseBitmap;
+
+class SkDrawBitmapShader : public SkDrawShader {
+    DECLARE_DRAW_MEMBER_INFO(BitmapShader);
+    SkDrawBitmapShader();
+    virtual bool add();
+    virtual SkShader* getShader();
+protected:
+    SkBool filterBitmap;
+    SkBaseBitmap* image;
+private:
+    typedef SkDrawShader INHERITED;
+};
+
+#endif // SkDrawShader_DEFINED
diff --git a/legacy/src/animator/SkDrawText.cpp b/legacy/src/animator/SkDrawText.cpp
new file mode 100644
index 0000000..b8d38b4
--- /dev/null
+++ b/legacy/src/animator/SkDrawText.cpp
@@ -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.
+ */
+
+
+#include "SkDrawText.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+enum SkText_Properties {
+    SK_PROPERTY(length)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkText::fInfo[] = {
+    SK_MEMBER_PROPERTY(length, Int),
+    SK_MEMBER(text, String),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkText);
+
+SkText::SkText() : x(0), y(0) {
+}
+
+SkText::~SkText() {
+}
+
+bool SkText::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawText(text.c_str(), text.size(), x, y, *maker.fPaint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkText::dump(SkAnimateMaker* maker) {
+    INHERITED::dump(maker);
+}
+#endif
+
+bool SkText::getProperty(int index, SkScriptValue* value) const {
+    SkASSERT(index == SK_PROPERTY(length));
+    value->fType = SkType_Int;
+    value->fOperand.fS32 = (int32_t) text.size();
+    return true;
+}
+
diff --git a/legacy/src/animator/SkDrawText.h b/legacy/src/animator/SkDrawText.h
new file mode 100644
index 0000000..7dd2e26
--- /dev/null
+++ b/legacy/src/animator/SkDrawText.h
@@ -0,0 +1,36 @@
+
+/*
+ * 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 SkDrawText_DEFINED
+#define SkDrawText_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+
+class SkText : public SkBoundable {
+    DECLARE_MEMBER_INFO(Text);
+    SkText();
+    virtual ~SkText();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool getProperty(int index, SkScriptValue* value) const ; 
+    const char* getText() { return text.c_str(); }
+    size_t getSize() { return text.size(); }
+protected:
+    SkString text;
+    SkScalar x;
+    SkScalar y;
+private:
+    friend class SkTextToPath;
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkDrawText_DEFINED
diff --git a/legacy/src/animator/SkDrawTextBox.cpp b/legacy/src/animator/SkDrawTextBox.cpp
new file mode 100644
index 0000000..d97dc5c
--- /dev/null
+++ b/legacy/src/animator/SkDrawTextBox.cpp
@@ -0,0 +1,82 @@
+
+/*
+ * 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 "SkDrawTextBox.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+enum SkDrawTextBox_Properties {
+    foo = 100,
+    SK_PROPERTY(spacingAlign),
+    SK_PROPERTY(mode)
+};
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawTextBox::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(mode, TextBoxMode),
+    SK_MEMBER_ALIAS(spacingAdd, fSpacingAdd, Float),
+    SK_MEMBER(spacingAlign, TextBoxAlign),
+    SK_MEMBER_ALIAS(spacingMul, fSpacingMul, Float),
+    SK_MEMBER_ALIAS(text, fText, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawTextBox);
+
+SkDrawTextBox::SkDrawTextBox()
+{
+    fSpacingMul     = SK_Scalar1;
+    fSpacingAdd     = 0;
+    spacingAlign    = SkTextBox::kStart_SpacingAlign;
+    mode            = SkTextBox::kLineBreak_Mode;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawTextBox::dump(SkAnimateMaker* maker)
+{
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (mode == 0) 
+        SkDebugf("mode=\"oneLine\" ");
+    if (spacingAlign == 1)
+        SkDebugf("spacingAlign=\"center\" ");
+    else if (spacingAlign == 2)
+        SkDebugf("spacingAlign=\"end\" ");
+    SkDebugf("/>\n");
+}
+#endif
+
+bool SkDrawTextBox::getProperty(int index, SkScriptValue* value) const
+{
+    return this->INHERITED::getProperty(index, value);
+}
+
+bool SkDrawTextBox::setProperty(int index, SkScriptValue& scriptValue)
+{
+    return this->INHERITED::setProperty(index, scriptValue);
+}
+
+bool SkDrawTextBox::draw(SkAnimateMaker& maker)
+{
+    SkTextBox   box;
+    box.setMode((SkTextBox::Mode) mode);
+    box.setSpacingAlign((SkTextBox::SpacingAlign) spacingAlign);
+    box.setBox(fRect);
+    box.setSpacing(fSpacingMul, fSpacingAdd);
+    SkBoundableAuto boundable(this, maker);
+    box.draw(maker.fCanvas, fText.c_str(), fText.size(), *maker.fPaint);
+    return false;
+}
+
+
diff --git a/legacy/src/animator/SkDrawTextBox.h b/legacy/src/animator/SkDrawTextBox.h
new file mode 100644
index 0000000..d8d7f0c
--- /dev/null
+++ b/legacy/src/animator/SkDrawTextBox.h
@@ -0,0 +1,39 @@
+
+/*
+ * 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 SkDrawTextBox_DEFINED
+#define SkDrawTextBox_DEFINED
+
+#include "SkDrawRectangle.h"
+#include "SkTextBox.h"
+
+class SkDrawTextBox : public SkDrawRect {
+    DECLARE_DRAW_MEMBER_INFO(TextBox);
+    SkDrawTextBox();
+
+    // overrides
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setProperty(int index, SkScriptValue& );
+
+private:
+    SkString fText;
+    SkScalar fSpacingMul;
+    SkScalar fSpacingAdd;
+    int /*SkTextBox::Mode*/  mode;
+    int /*SkTextBox::SpacingAlign*/ spacingAlign;
+
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDrawTextBox_DEFINED
+
diff --git a/legacy/src/animator/SkDrawTo.cpp b/legacy/src/animator/SkDrawTo.cpp
new file mode 100644
index 0000000..bc5cd6d
--- /dev/null
+++ b/legacy/src/animator/SkDrawTo.cpp
@@ -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.
+ */
+
+
+#include "SkDrawTo.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawBitmap.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawTo::fInfo[] = {
+    SK_MEMBER(drawOnce, Boolean),
+    SK_MEMBER(use, Bitmap)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawTo);
+
+SkDrawTo::SkDrawTo() : drawOnce(false), use(NULL), fDrawnOnce(false) {
+}
+
+#if 0
+SkDrawTo::~SkDrawTo() {
+    SkASSERT(0);
+}
+#endif
+
+bool SkDrawTo::draw(SkAnimateMaker& maker) {
+    if (fDrawnOnce)
+        return false;
+    SkCanvas canvas(use->fBitmap);
+    SkCanvas* save = maker.fCanvas;
+    maker.fCanvas = &canvas;
+    INHERITED::draw(maker);
+    maker.fCanvas = save;
+    fDrawnOnce = drawOnce;
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawTo::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (use)
+        SkDebugf("use=\"%s\" ", use->id);
+    dumpDrawables(maker);
+}
+#endif
+
diff --git a/legacy/src/animator/SkDrawTo.h b/legacy/src/animator/SkDrawTo.h
new file mode 100644
index 0000000..b6365af
--- /dev/null
+++ b/legacy/src/animator/SkDrawTo.h
@@ -0,0 +1,34 @@
+
+/*
+ * 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 SkDrawTo_DEFINED
+#define SkDrawTo_DEFINED
+
+#include "SkDrawGroup.h"
+#include "SkMemberInfo.h"
+
+class SkDrawBitmap;
+
+class SkDrawTo : public SkGroup {
+    DECLARE_MEMBER_INFO(DrawTo);
+    SkDrawTo();
+//  virtual ~SkDrawTo();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+protected:
+    SkBool drawOnce;
+    SkDrawBitmap* use;
+private:
+    typedef SkGroup INHERITED;
+    SkBool fDrawnOnce;
+};
+
+#endif // SkDrawTo_DEFINED
diff --git a/legacy/src/animator/SkDrawTransparentShader.cpp b/legacy/src/animator/SkDrawTransparentShader.cpp
new file mode 100644
index 0000000..bb5392a
--- /dev/null
+++ b/legacy/src/animator/SkDrawTransparentShader.cpp
@@ -0,0 +1,16 @@
+
+/*
+ * 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 "SkDrawTransparentShader.h"
+#include "SkTransparentShader.h"
+
+SkShader* SkDrawTransparentShader::getShader() {
+    return new SkTransparentShader();
+}
+
diff --git a/legacy/src/animator/SkDrawTransparentShader.h b/legacy/src/animator/SkDrawTransparentShader.h
new file mode 100644
index 0000000..df1c6eb
--- /dev/null
+++ b/legacy/src/animator/SkDrawTransparentShader.h
@@ -0,0 +1,21 @@
+
+/*
+ * 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 SkDrawTransparentShader_DEFINED
+#define SkDrawTransparentShader_DEFINED
+
+#include "SkPaintParts.h"
+
+class SkDrawTransparentShader : public SkDrawShader {
+    DECLARE_EMPTY_MEMBER_INFO(TransparentShader);
+    virtual SkShader* getShader();
+};
+
+#endif // SkDrawTransparentShader_DEFINED
+
diff --git a/legacy/src/animator/SkDrawable.cpp b/legacy/src/animator/SkDrawable.cpp
new file mode 100644
index 0000000..8db0333
--- /dev/null
+++ b/legacy/src/animator/SkDrawable.cpp
@@ -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.
+ */
+
+
+#include "SkDrawable.h"
+
+bool SkDrawable::doEvent(SkDisplayEvent::Kind , SkEventState* ) {
+    return false;
+}
+
+bool SkDrawable::isDrawable() const { 
+    return true; 
+}
+
+void SkDrawable::initialize() {
+}
+
+void SkDrawable::setSteps(int steps) {
+}
+
diff --git a/legacy/src/animator/SkDrawable.h b/legacy/src/animator/SkDrawable.h
new file mode 100644
index 0000000..139ce85
--- /dev/null
+++ b/legacy/src/animator/SkDrawable.h
@@ -0,0 +1,28 @@
+
+/*
+ * 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 SkDrawable_DEFINED
+#define SkDrawable_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkDisplayEvent.h"
+#include "SkMath.h"
+
+struct SkEventState;
+
+class SkDrawable :  public SkDisplayable {
+public:
+    virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state );
+    virtual bool draw(SkAnimateMaker& ) = 0; 
+    virtual void initialize();
+    virtual bool isDrawable() const;
+    virtual void setSteps(int steps);
+};
+
+#endif // SkDrawable_DEFINED
diff --git a/legacy/src/animator/SkDump.cpp b/legacy/src/animator/SkDump.cpp
new file mode 100644
index 0000000..bd91277
--- /dev/null
+++ b/legacy/src/animator/SkDump.cpp
@@ -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.
+ */
+
+
+#include "SkDump.h"
+
+#ifdef SK_DUMP_ENABLED
+
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayEvents.h"
+#include "SkDisplayList.h"
+#include "SkString.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDump::fInfo[] = {
+    SK_MEMBER(displayList, Boolean),
+    SK_MEMBER(eventList, Boolean),
+    SK_MEMBER(events, Boolean),
+    SK_MEMBER(groups, Boolean),
+    SK_MEMBER(name, String),
+    SK_MEMBER(posts, Boolean),
+    SK_MEMBER(script, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDump);
+
+SkDump::SkDump() : displayList(-1), eventList(-1), events(-1), groups(-1), posts(-1) {
+}
+
+bool SkDump::enable(SkAnimateMaker& maker ) {
+    if (script.size() > 0)
+        return evaluate(maker);
+    bool hasAttr = false;
+    if (events > 0)
+        hasAttr |= maker.fDumpEvents = true;
+    if (posts > 0)
+        hasAttr |= maker.fDumpPosts = true;
+    if (groups > 0)
+        hasAttr |= maker.fDumpGConditions = true;
+    if ((hasAttr |= (eventList > 0)) == true)
+        maker.fEvents.dump(maker);
+    if ((hasAttr |= (name.size() > 0)) == true)
+        maker.dump(name.c_str());
+    if (displayList > 0 || (displayList != 0 && hasAttr == false))
+        maker.fDisplayList.dump(&maker);
+    return true;
+}
+
+bool SkDump::evaluate(SkAnimateMaker &maker) {
+    SkAnimatorScript scriptEngine(maker, NULL, SkType_Int);
+    SkScriptValue value;
+    const char* cScript = script.c_str();
+    bool success = scriptEngine.evaluateScript(&cScript, &value);
+    SkDebugf("%*s<dump script=\"%s\" answer=\" ", SkDisplayList::fIndent, "", script.c_str());
+    if (success == false) {
+        SkDebugf("INVALID\" />\n");
+        return false;
+    }
+    switch (value.fType) {
+        case SkType_Float:
+            SkDebugf("%g\" />\n", SkScalarToFloat(value.fOperand.fScalar));
+            break;
+        case SkType_Int:
+            SkDebugf("%d\" />\n", value.fOperand.fS32);
+            break;
+        case SkType_String:
+            SkDebugf("%s\" />\n", value.fOperand.fString->c_str());
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool SkDump::hasEnable() const {
+    return true;
+}
+
+void SkDump::GetEnumString(SkDisplayTypes type, int index, SkString* result) {
+    int badEnum = index;
+    const SkDisplayEnumMap& map = SkAnimatorScript::GetEnumValues(type);
+    const char* str  = map.fValues;
+    while (--index >= 0) {
+        str = strchr(str, '|');
+        if (str == NULL) {
+            result->reset();
+            result->appendS32(badEnum);
+            return;
+        }
+        str += 1;
+    }
+    const char* end = strchr(str, '|');
+    if (end == NULL)
+        end = str + strlen(str);
+    result->set(str, end - str);
+}
+
+#else
+
+// in the release version, <dump> is allowed, and its attributes are defined, but
+// are not stored and have no effect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+enum SkDump_Properties {
+    SK_PROPERTY(displayList),
+    SK_PROPERTY(eventList),
+    SK_PROPERTY(events),
+    SK_PROPERTY(groups),
+    SK_PROPERTY(name),
+    SK_PROPERTY(posts),
+    SK_PROPERTY(script)
+};
+
+const SkMemberInfo SkDump::fInfo[] = {
+    SK_MEMBER_PROPERTY(displayList, Boolean),
+    SK_MEMBER_PROPERTY(eventList, Boolean),
+    SK_MEMBER_PROPERTY(events, Boolean),
+    SK_MEMBER_PROPERTY(groups, Boolean),
+    SK_MEMBER_PROPERTY(name, String),
+    SK_MEMBER_PROPERTY(posts, Boolean),
+    SK_MEMBER_PROPERTY(script, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDump);
+
+bool SkDump::enable(SkAnimateMaker& maker ) {
+    return true;
+}
+
+bool SkDump::hasEnable() const {
+    return true;
+}
+
+bool SkDump::setProperty(int index, SkScriptValue& ) {
+    return index <= SK_PROPERTY(posts); 
+}
+
+#endif
diff --git a/legacy/src/animator/SkDump.h b/legacy/src/animator/SkDump.h
new file mode 100644
index 0000000..3222f06
--- /dev/null
+++ b/legacy/src/animator/SkDump.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 SkDump_DEFINED
+#define SkDump_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkAnimateMaker;
+class SkString;
+
+class SkDump : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Dump);
+#ifdef SK_DUMP_ENABLED
+    SkDump();
+    virtual bool enable(SkAnimateMaker & );
+    bool evaluate(SkAnimateMaker &);
+    virtual bool hasEnable() const;
+    static void GetEnumString(SkDisplayTypes , int index, SkString* result);
+    SkBool displayList;
+    SkBool eventList;
+    SkBool events;
+    SkString name;
+    SkBool groups;
+    SkBool posts;
+    SkString script;
+#else
+    virtual bool enable(SkAnimateMaker & );
+    virtual bool hasEnable() const;
+    virtual bool setProperty(int index, SkScriptValue& );
+#endif
+};
+
+
+#endif // SkDump_DEFINED
+
diff --git a/legacy/src/animator/SkExtraPathEffects.xsd b/legacy/src/animator/SkExtraPathEffects.xsd
new file mode 100644
index 0000000..9592443
--- /dev/null
+++ b/legacy/src/animator/SkExtraPathEffects.xsd
@@ -0,0 +1,33 @@
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

+xmlns:Sk="urn:screenplay"

+xmlns:extra="urn:extraPathEffects" targetNamespace="urn:extraPathEffects" >

+	<xs:import namespace="urn:screenplay"

+		schemaLocation="SkAnimateSchema.xsd" />

+		

+	<xs:element name="composePathEffect" >

+		<xs:complexType>

+			<xs:choice maxOccurs="1">

+				<xs:element ref="Sk:dash"/>

+				<xs:element ref="extra:shape1DPathEffect"/>

+			</xs:choice>

+			<xs:attribute name="id" type="xs:ID"/>

+		</xs:complexType>

+	</xs:element>

+

+	<xs:element name="shape1DPathEffect" >

+		<xs:complexType>

+			<xs:choice maxOccurs="1">

+				<xs:element ref="Sk:matrix"/>

+				<xs:element ref="Sk:path"/>

+			</xs:choice>

+			<xs:attribute name="addPath" type="Sk:DynamicString" />

+			<xs:attribute name="matrix" type="Sk:DynamicString" />

+			<xs:attribute name="path" type="Sk:Path" />

+			<xs:attribute name="phase" type="Sk:DynamicString"/>

+			<xs:attribute name="spacing" type="Sk:DynamicString"/>

+			<xs:attribute name="id" type="xs:ID"/>

+		</xs:complexType>

+	</xs:element>

+		

+</xs:schema>

+
diff --git a/legacy/src/animator/SkExtras.h b/legacy/src/animator/SkExtras.h
new file mode 100644
index 0000000..dcd3905
--- /dev/null
+++ b/legacy/src/animator/SkExtras.h
@@ -0,0 +1,34 @@
+
+/*
+ * 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 SkExtras_DEFINED
+#define SkExtras_DEFINED
+
+#include "SkScript.h"
+
+class SkExtras {
+public:
+            SkExtras();
+    virtual ~SkExtras() {}
+
+    virtual SkDisplayable* createInstance(SkDisplayTypes type) = 0;
+    virtual bool definesType(SkDisplayTypes type) = 0;
+#if SK_USE_CONDENSED_INFO == 0
+    virtual const SkMemberInfo* getMembers(SkDisplayTypes type, int* infoCountPtr) = 0;
+#endif
+#ifdef SK_DEBUG
+    virtual const char* getName(SkDisplayTypes type) = 0;
+#endif
+    virtual SkDisplayTypes getType(const char match[], size_t len ) = 0;
+
+    SkScriptEngine::_propertyCallBack fExtraCallBack;
+    void* fExtraStorage;
+};
+
+#endif // SkExtras_DEFINED
diff --git a/legacy/src/animator/SkGetCondensedInfo.cpp b/legacy/src/animator/SkGetCondensedInfo.cpp
new file mode 100644
index 0000000..a583cef
--- /dev/null
+++ b/legacy/src/animator/SkGetCondensedInfo.cpp
@@ -0,0 +1,122 @@
+
+/*
+ * 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 "SkMemberInfo.h"
+
+#if SK_USE_CONDENSED_INFO == 1
+
+// SkCondensed.cpp is auto-generated
+// To generate it, execute SkDisplayType::BuildCondensedInfo()
+#ifdef SK_DEBUG
+#include "SkCondensedDebug.cpp"
+#else
+#include "SkCondensedRelease.cpp"
+#endif
+
+static int _searchByName(const unsigned char* lengths, int count, const char* strings, const char target[]) {
+    int lo = 0;
+    int hi = count - 1;
+    while (lo < hi) {
+        int mid = (hi + lo) >> 1;
+        if (strcmp(&strings[lengths[mid << 2]], target) < 0)
+            lo = mid + 1;
+        else 
+            hi = mid;
+    }
+    if (strcmp(&strings[lengths[hi << 2]], target) != 0)
+        return -1;
+    return hi;
+}
+
+static int _searchByType(SkDisplayTypes type) {
+    unsigned char match = (unsigned char) type;
+    int lo = 0;
+    int hi = kTypeIDs - 1;
+    while (lo < hi) {
+        int mid = (hi + lo) >> 1;
+        if (gTypeIDs[mid] < match)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+    if (gTypeIDs[hi] != type)
+        return -1;
+    return hi;
+}
+
+const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* , SkDisplayTypes type, int* infoCountPtr) {
+    int lookup = _searchByType(type);
+    if (lookup < 0)
+        return NULL;
+    if (infoCountPtr)
+        *infoCountPtr = gInfoCounts[lookup];
+    return gInfoTables[lookup];
+}
+
+// !!! replace with inline
+const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* , SkDisplayTypes type, const char** matchPtr ) {
+    const SkMemberInfo* info = SkMemberInfo::Find(type, matchPtr);
+    SkASSERT(info);
+    return info;
+}
+
+static const SkMemberInfo* _lookup(int lookup, const char** matchPtr) {
+    int count = gInfoCounts[lookup];
+    const SkMemberInfo* info = gInfoTables[lookup];
+    if (info->fType == SkType_BaseClassInfo) {
+        int baseTypeLookup = info->fOffset;
+        const SkMemberInfo* result = _lookup(baseTypeLookup, matchPtr);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    const char* match = *matchPtr;
+    const char* strings = gInfoNames[lookup];
+    int index = _searchByName(&info->fName, count, strings, match);
+    if (index < 0)
+        return NULL;
+    return &info[index];
+}
+
+const SkMemberInfo* SkMemberInfo::Find(SkDisplayTypes type, int* index) {
+    int count = gInfoCounts[lookup];
+    const SkMemberInfo* info = gInfoTables[lookup];
+    if (info->fType == SkType_BaseClassInfo) {
+        int baseTypeLookup = info->fOffset;
+        const SkMemberInfo* result = Find(baseTypeLookup, index);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    if (*index >= count) {
+        *index -= count;
+        return NULL;
+    }
+    return &info[index];
+}
+
+const SkMemberInfo* SkMemberInfo::Find(SkDisplayTypes type, const char** matchPtr) {
+    int lookup = _searchByType(type);
+    SkASSERT(lookup >= 0);
+    return _lookup(lookup, matchPtr);
+}
+
+const SkMemberInfo* SkMemberInfo::getInherited() const {
+    int baseTypeLookup = fOffset;
+    return gInfoTables[baseTypeLookup];
+}
+
+#endif
+
diff --git a/legacy/src/animator/SkHitClear.cpp b/legacy/src/animator/SkHitClear.cpp
new file mode 100644
index 0000000..70b3e73
--- /dev/null
+++ b/legacy/src/animator/SkHitClear.cpp
@@ -0,0 +1,33 @@
+
+/*
+ * 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 "SkHitClear.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkHitClear::fInfo[] = {
+    SK_MEMBER_ARRAY(targets, Displayable)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkHitClear);
+
+bool SkHitClear::enable(SkAnimateMaker& maker) {
+    for (int tIndex = 0; tIndex < targets.count(); tIndex++) {
+        SkDisplayable* target = targets[tIndex];
+        target->clearBounder();
+    }
+    return true;
+}
+
+bool SkHitClear::hasEnable() const {
+    return true;
+}
+
diff --git a/legacy/src/animator/SkHitClear.h b/legacy/src/animator/SkHitClear.h
new file mode 100644
index 0000000..9c40209
--- /dev/null
+++ b/legacy/src/animator/SkHitClear.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 SkHitClear_DEFINED
+#define SkHitClear_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkTypedArray.h"
+
+class SkHitClear : public SkDisplayable {
+    DECLARE_MEMBER_INFO(HitClear);
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+private:
+    SkTDDisplayableArray targets;
+};
+
+#endif // SkHitClear_DEFINED
diff --git a/legacy/src/animator/SkHitTest.cpp b/legacy/src/animator/SkHitTest.cpp
new file mode 100644
index 0000000..dc4e739
--- /dev/null
+++ b/legacy/src/animator/SkHitTest.cpp
@@ -0,0 +1,75 @@
+
+/*
+ * 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 "SkHitTest.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkHitTest::fInfo[] = {
+    SK_MEMBER_ARRAY(bullets, Displayable),
+    SK_MEMBER_ARRAY(hits, Int),
+    SK_MEMBER_ARRAY(targets, Displayable),
+    SK_MEMBER(value, Boolean)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkHitTest);
+
+SkHitTest::SkHitTest() : value(false) {
+}
+
+bool SkHitTest::draw(SkAnimateMaker& maker) {
+    hits.setCount(bullets.count());
+    value = false;
+    int bulletCount = bullets.count();
+    int targetCount = targets.count();
+    for (int bIndex = 0; bIndex < bulletCount; bIndex++) {
+        SkDisplayable* bullet = bullets[bIndex];
+        SkRect bBounds;
+        bullet->getBounds(&bBounds);
+        hits[bIndex] = -1;
+        if (bBounds.fLeft == (int16_t)0x8000U)
+            continue;
+        for (int tIndex = 0; tIndex < targetCount; tIndex++) {
+            SkDisplayable* target = targets[tIndex];
+            SkRect tBounds;
+            target->getBounds(&tBounds);
+            if (bBounds.intersect(tBounds)) {
+                hits[bIndex] = tIndex;
+                value = true;
+                break;
+            }
+        }
+    }
+    return false;
+}
+
+bool SkHitTest::enable(SkAnimateMaker& maker) {
+    for (int bIndex = 0; bIndex < bullets.count(); bIndex++) {
+        SkDisplayable* bullet = bullets[bIndex];
+        bullet->enableBounder();
+    }
+    for (int tIndex = 0; tIndex < targets.count(); tIndex++) {
+        SkDisplayable* target = targets[tIndex];
+        target->enableBounder();
+    }
+    return false;
+}
+
+bool SkHitTest::hasEnable() const {
+    return true;
+}
+
+const SkMemberInfo* SkHitTest::preferredChild(SkDisplayTypes type) {
+    if (bullets.count() == 0)
+        return getMember("bullets");
+    return getMember("targets"); // !!! cwap! need to refer to member through enum like kScope instead
+}
+
diff --git a/legacy/src/animator/SkHitTest.h b/legacy/src/animator/SkHitTest.h
new file mode 100644
index 0000000..68d5cc5
--- /dev/null
+++ b/legacy/src/animator/SkHitTest.h
@@ -0,0 +1,30 @@
+
+/*
+ * 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 SkHitTest_DEFINED
+#define SkHitTest_DEFINED
+
+#include "SkDrawable.h"
+#include "SkTypedArray.h"
+
+class SkHitTest : public SkDrawable {
+    DECLARE_MEMBER_INFO(HitTest);
+    SkHitTest();
+    virtual bool draw(SkAnimateMaker& );
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+private:
+    SkTDDisplayableArray bullets;
+    SkTDIntArray hits;
+    SkTDDisplayableArray targets;
+    SkBool value;
+};
+
+#endif // SkHitTest_DEFINED
diff --git a/legacy/src/animator/SkIntArray.h b/legacy/src/animator/SkIntArray.h
new file mode 100644
index 0000000..6769a9e
--- /dev/null
+++ b/legacy/src/animator/SkIntArray.h
@@ -0,0 +1,58 @@
+
+/*
+ * 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 SkIntArray_DEFINED
+#define SkIntArray_DEFINED
+
+#include "SkColor.h"
+#include "SkDisplayType.h"
+#include "SkMath.h"
+#include "SkTDArray_Experimental.h"
+
+class SkActive;
+class SkAnimateBase;
+class SkDataInput;
+class SkDisplayable;
+class SkDisplayEvent;
+class SkDrawable;
+class SkDrawColor;
+class SkMatrixPart;
+struct SkMemberInfo;
+class SkPathPart;
+class SkPaintPart;
+class SkTypedArray;
+class SkString;
+union SkOperand;
+
+typedef SkIntArray(int) SkTDIntArray;
+typedef SkIntArray(SkColor) SkTDColorArray;
+typedef SkIntArray(SkDisplayTypes) SkTDDisplayTypesArray;
+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(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; 
+
+#endif // SkIntArray_DEFINED
+
+
+
diff --git a/legacy/src/animator/SkMatrixParts.cpp b/legacy/src/animator/SkMatrixParts.cpp
new file mode 100644
index 0000000..f00bb8e
--- /dev/null
+++ b/legacy/src/animator/SkMatrixParts.cpp
@@ -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.
+ */
+
+
+#include "SkMatrixParts.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawRectangle.h"
+#include "SkDrawPath.h"
+
+SkMatrixPart::SkMatrixPart() : fMatrix(NULL) {
+}
+
+void SkMatrixPart::dirty() { 
+    fMatrix->dirty(); 
+}
+
+SkDisplayable* SkMatrixPart::getParent() const {
+    return fMatrix;
+}
+
+bool SkMatrixPart::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->isMatrix() == false)
+        return true;
+    fMatrix = (SkDrawMatrix*) parent;
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRotate::fInfo[] = {
+    SK_MEMBER(center, Point),
+    SK_MEMBER(degrees, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRotate);
+
+SkRotate::SkRotate() : degrees(0) { 
+    center.fX = center.fY = 0; 
+}
+
+bool SkRotate::add() {
+    fMatrix->rotate(degrees, center);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkScale::fInfo[] = {
+    SK_MEMBER(center, Point),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkScale);
+
+SkScale::SkScale() : x(SK_Scalar1), y(SK_Scalar1) { 
+    center.fX = center.fY = 0; 
+}
+
+bool SkScale::add() {
+    fMatrix->scale(x, y, center);   
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSkew::fInfo[] = {
+    SK_MEMBER(center, Point),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSkew);
+
+SkSkew::SkSkew() : x(0), y(0) { 
+    center.fX = center.fY = 0; 
+}
+
+bool SkSkew::add() {
+    fMatrix->skew(x, y, center);    
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkTranslate::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkTranslate);
+
+SkTranslate::SkTranslate() : x(0), y(0) {
+}
+
+bool SkTranslate::add() {
+    fMatrix->translate(x, y);   
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkFromPath::fInfo[] = {
+    SK_MEMBER(mode, FromPathMode),
+    SK_MEMBER(offset, Float),
+    SK_MEMBER(path, Path)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkFromPath);
+
+SkFromPath::SkFromPath() : 
+    mode(0), offset(0), path(NULL) {
+}
+
+SkFromPath::~SkFromPath() {
+}
+
+bool SkFromPath::add() {
+    if (path == NULL)
+        return true;
+    static const uint8_t gFlags[] = {
+        SkPathMeasure::kGetPosAndTan_MatrixFlag,    // normal
+        SkPathMeasure::kGetTangent_MatrixFlag,      // angle
+        SkPathMeasure::kGetPosition_MatrixFlag      // position
+    };
+    if ((unsigned)mode >= SK_ARRAY_COUNT(gFlags))
+        return true;
+    SkMatrix result;
+    fPathMeasure.setPath(&path->getPath(), false);
+    if (fPathMeasure.getMatrix(offset, &result, (SkPathMeasure::MatrixFlags)gFlags[mode]))
+        fMatrix->set(result);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRectToRect::fInfo[] = {
+    SK_MEMBER(destination, Rect),
+    SK_MEMBER(source, Rect)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRectToRect);
+
+SkRectToRect::SkRectToRect() : 
+    source(NULL), destination(NULL) {
+}
+
+SkRectToRect::~SkRectToRect() {
+}
+
+bool SkRectToRect::add() {
+    if (source == NULL || destination == NULL)
+        return true;
+    SkMatrix temp;
+    temp.setRectToRect(source->fRect, destination->fRect,
+                       SkMatrix::kFill_ScaleToFit);
+    fMatrix->set(temp);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkRectToRect::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("/>\n");
+    SkDisplayList::fIndent += 4;
+    if (source) {
+        SkDebugf("%*s<source>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        source->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</source>\n", SkDisplayList::fIndent, "");
+    }
+    if (destination) {
+        SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        destination->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");        
+    }
+    SkDisplayList::fIndent -= 4;
+    dumpEnd(maker);
+}
+#endif
+
+const SkMemberInfo* SkRectToRect::preferredChild(SkDisplayTypes ) {
+    if (source == NULL)
+        return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead
+    else {
+        SkASSERT(destination == NULL);
+        return getMember("destination");
+    }
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPolyToPoly::fInfo[] = {
+    SK_MEMBER(destination, Polygon),
+    SK_MEMBER(source, Polygon)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPolyToPoly);
+
+SkPolyToPoly::SkPolyToPoly() : source(NULL), destination(NULL) {
+}
+
+SkPolyToPoly::~SkPolyToPoly() {
+}
+
+bool SkPolyToPoly::add() {
+    SkASSERT(source);
+    SkASSERT(destination);
+    SkPoint src[4];
+    SkPoint dst[4];
+    SkPath& sourcePath = source->getPath();
+    int srcPts = sourcePath.getPoints(src, 4);
+    SkPath& destPath = destination->getPath();
+    int dstPts = destPath.getPoints(dst, 4);
+    if (srcPts != dstPts)
+        return true;
+    SkMatrix temp;
+    temp.setPolyToPoly(src, dst, srcPts);
+    fMatrix->set(temp);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkPolyToPoly::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("/>\n");
+    SkDisplayList::fIndent += 4;
+    if (source) {
+        SkDebugf("%*s<source>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        source->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</source>\n", SkDisplayList::fIndent, "");
+    }
+    if (destination) {
+        SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        destination->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");        
+    }
+    SkDisplayList::fIndent -= 4;
+    dumpEnd(maker);
+}
+#endif
+
+void SkPolyToPoly::onEndElement(SkAnimateMaker& ) {
+    SkASSERT(source);
+    SkASSERT(destination);
+    if (source->childHasID() || destination->childHasID())
+        fMatrix->setChildHasID();
+}
+
+const SkMemberInfo* SkPolyToPoly::preferredChild(SkDisplayTypes ) {
+    if (source == NULL)
+        return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead
+    else {
+        SkASSERT(destination == NULL);
+        return getMember("destination");
+    }
+}
+
+
diff --git a/legacy/src/animator/SkMatrixParts.h b/legacy/src/animator/SkMatrixParts.h
new file mode 100644
index 0000000..5f6cd54
--- /dev/null
+++ b/legacy/src/animator/SkMatrixParts.h
@@ -0,0 +1,119 @@
+
+/*
+ * 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 SkMatrixParts_DEFINED
+#define SkMatrixParts_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkPathMeasure.h"
+
+class SkDrawPath;
+class SkDrawRect;
+class SkPolygon;
+
+class SkDrawMatrix;
+// class SkMatrix;
+
+class SkMatrixPart : public SkDisplayable {
+public:
+    SkMatrixPart();
+    virtual bool add() = 0;
+    virtual void dirty();
+    virtual SkDisplayable* getParent() const;
+    virtual bool setParent(SkDisplayable* parent);
+#ifdef SK_DEBUG
+    virtual bool isMatrixPart() const { return true; }
+#endif
+protected:
+    SkDrawMatrix* fMatrix;
+};
+
+class SkRotate : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Rotate);
+    SkRotate();
+protected:
+    virtual bool add();
+    SkScalar degrees;
+    SkPoint center;
+};
+
+class SkScale : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Scale);
+    SkScale();
+protected:
+    virtual bool add();
+    SkScalar x;
+    SkScalar y;
+    SkPoint center;
+};
+
+class SkSkew : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Skew);
+    SkSkew();
+protected:
+    virtual bool add();
+    SkScalar x;
+    SkScalar y;
+    SkPoint center;
+};
+
+class SkTranslate : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Translate);
+    SkTranslate();
+protected:
+    virtual bool add();
+    SkScalar x;
+    SkScalar y;
+};
+
+class SkFromPath : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(FromPath);
+    SkFromPath();
+    virtual ~SkFromPath();
+protected:
+    virtual bool add();
+    int32_t mode;
+    SkScalar offset;
+    SkDrawPath* path;
+    SkPathMeasure fPathMeasure;
+};
+
+class SkRectToRect : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(RectToRect);
+    SkRectToRect();
+    virtual ~SkRectToRect();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+protected:
+    virtual bool add();
+    SkDrawRect* source;
+    SkDrawRect* destination;
+};
+
+class SkPolyToPoly : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(PolyToPoly);
+    SkPolyToPoly();
+    virtual ~SkPolyToPoly();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+protected:
+    virtual bool add();
+    SkPolygon* source;
+    SkPolygon* destination;
+};
+
+// !!! add concat matrix ? 
+
+#endif // SkMatrixParts_DEFINED
diff --git a/legacy/src/animator/SkMemberInfo.cpp b/legacy/src/animator/SkMemberInfo.cpp
new file mode 100644
index 0000000..7fae503
--- /dev/null
+++ b/legacy/src/animator/SkMemberInfo.cpp
@@ -0,0 +1,561 @@
+
+/*
+ * 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 "SkMemberInfo.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkBase64.h"
+#include "SkCamera.h"
+#include "SkDisplayable.h"
+#include "SkDisplayTypes.h"
+#include "SkDraw3D.h"
+#include "SkDrawColor.h"
+#include "SkParse.h"
+#include "SkScript.h"
+#include "SkTSearch.h"
+#include "SkTypedArray.h"
+
+size_t SkMemberInfo::GetSize(SkDisplayTypes type) { // size of simple types only
+    size_t byteSize;
+    switch (type) {
+        case SkType_ARGB:
+            byteSize = sizeof(SkColor);
+            break;
+        case SkType_AddMode:
+        case SkType_Align:
+        case SkType_ApplyMode:
+        case SkType_ApplyTransition:
+        case SkType_BitmapEncoding:
+        case SkType_Boolean:
+        case SkType_Cap:
+        case SkType_EventCode:
+        case SkType_EventKind:
+        case SkType_EventMode:
+        case SkType_FilterType:
+        case SkType_FontStyle:
+        case SkType_FromPathMode:
+        case SkType_Join:
+        case SkType_MaskFilterBlurStyle:
+        case SkType_PathDirection:
+        case SkType_Style:
+        case SkType_TileMode:
+        case SkType_Xfermode:
+            byteSize = sizeof(int);
+            break;
+        case SkType_Base64: // assume base64 data is always const, copied by ref
+        case SkType_Displayable:
+        case SkType_Drawable:
+        case SkType_Matrix:
+            byteSize = sizeof(void*); 
+            break;
+        case SkType_MSec:
+            byteSize = sizeof(SkMSec);
+            break;
+        case SkType_Point:
+            byteSize = sizeof(SkPoint);
+            break;
+        case SkType_3D_Point:
+            byteSize = sizeof(Sk3D_Point);
+            break;
+        case SkType_Int:
+            byteSize = sizeof(int32_t);
+            break;
+        case SkType_Float:
+            byteSize = sizeof(SkScalar);
+            break;
+        case SkType_DynamicString:
+        case SkType_String:
+            byteSize = sizeof(SkString);    // assume we'll copy by reference, not value
+            break;
+        default:
+//          SkASSERT(0);
+            byteSize = 0;
+    }
+    return byteSize;
+}
+
+bool SkMemberInfo::getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const {
+    SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
+    char* valuePtr = (char*) *(SkOperand**) memberData(displayable);
+    SkDisplayTypes type = (SkDisplayTypes) 0;
+    if (displayable->getType() == SkType_Array) {
+        SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
+        if (dispArray->values.count() <= index)
+            return false;
+        type = dispArray->values.getType();
+    } else {
+        SkASSERT(0); // incomplete
+    }
+    size_t byteSize = GetSize(type);
+    memcpy(value, valuePtr + index * byteSize, byteSize);
+    return true;
+}
+
+size_t SkMemberInfo::getSize(const SkDisplayable* displayable) const {
+    size_t byteSize;
+    switch (fType) {
+        case SkType_MemberProperty:
+            byteSize = GetSize(propertyType());
+            break;
+        case SkType_Array: {
+            SkDisplayTypes type;
+            if (displayable == NULL)
+                return sizeof(int);
+            if (displayable->getType() == SkType_Array) {
+                SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
+                type = dispArray->values.getType();
+            } else
+                type = propertyType();
+            SkTDOperandArray* array = (SkTDOperandArray*) memberData(displayable);
+            byteSize = GetSize(type) * array->count();
+            } break;
+        default:
+            byteSize = GetSize((SkDisplayTypes) fType);
+    }
+    return byteSize;
+}
+
+void SkMemberInfo::getString(const SkDisplayable* displayable, SkString** string) const {
+    if (fType == SkType_MemberProperty) {
+        SkScriptValue value;
+        displayable->getProperty(propertyIndex(), &value);
+        SkASSERT(value.fType == SkType_String);
+        *string = value.fOperand.fString;
+        return;
+    }
+    SkASSERT(fCount == sizeof(SkString) / sizeof(SkScalar));
+    SkASSERT(fType == SkType_String || fType == SkType_DynamicString);
+    void* valuePtr = memberData(displayable);
+    *string = (SkString*) valuePtr;
+}
+
+void SkMemberInfo::getValue(const SkDisplayable* displayable, SkOperand value[], int count) const {
+    SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
+    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
+    memcpy(value, valuePtr, byteSize);
+}
+
+void SkMemberInfo::setString(SkDisplayable* displayable, SkString* value) const {
+    SkString* string = (SkString*) memberData(displayable);
+    string->set(*value);
+    displayable->dirty();
+}
+
+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);
+    if (fType == SkType_Array) {
+        SkTDScalarArray* array = (SkTDScalarArray* ) dst;
+        array->setCount(count);
+        dst = (char*) array->begin();
+    }
+    memcpy(dst, values, count * sizeof(SkOperand));
+    displayable->dirty();
+}
+
+                            
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_hex(int c)
+{
+    if (is_between(c, '0', '9'))
+        return true;
+    c |= 0x20;  // make us lower-case
+    if (is_between(c, 'a', 'f'))
+        return true;
+    return false;
+}
+
+
+bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, 
+    int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
+    const char rawValue[], size_t rawValueLen) const 
+{
+    SkString valueStr(rawValue, rawValueLen);
+    SkScriptValue scriptValue;
+    scriptValue.fType = SkType_Unknown;
+    scriptValue.fOperand.fS32 = 0;
+    SkDisplayTypes type = getType();
+    SkAnimatorScript engine(maker, displayable, type);
+    if (arrayStorage)
+        displayable = NULL;
+    bool success = true;
+    void* untypedStorage = NULL;
+    if (displayable && fType != SkType_MemberProperty && fType != SkType_MemberFunction)
+        untypedStorage = (SkTDOperandArray*) memberData(displayable);
+
+    if (type == SkType_ARGB) {
+        // for both SpiderMonkey and SkiaScript, substitute any #xyz or #xxyyzz first
+            // it's enough to expand the colors into 0xFFxxyyzz
+        const char* poundPos;
+        while ((poundPos = strchr(valueStr.c_str(), '#')) != NULL) {
+            size_t offset = poundPos - valueStr.c_str();
+            if (valueStr.size() - offset < 4)
+                break;
+            char r = poundPos[1];
+            char g = poundPos[2];
+            char b = poundPos[3];
+            if (is_hex(r) == false || is_hex(g) == false || is_hex(b) == false)
+                break;
+            char hex = poundPos[4];
+            if (is_hex(hex) == false) {
+                valueStr.insertUnichar(offset + 1, r);
+                valueStr.insertUnichar(offset + 3, g);
+                valueStr.insertUnichar(offset + 5, b);
+            }
+            *(char*) poundPos = '0'; // overwrite '#'
+            valueStr.insert(offset + 1, "xFF");
+        } 
+    }
+    if (SkDisplayType::IsDisplayable(&maker, type) || SkDisplayType::IsEnum(&maker, type) || type == SkType_ARGB)
+        goto scriptCommon;
+    switch (type) {
+        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 
+#endif
+            if (strncmp(rawValue, "#script:", sizeof("#script:") - 1) != 0)
+                goto noScriptString;
+            valueStr.remove(0, 8);
+        case SkType_Unknown:
+        case SkType_Int: 
+        case SkType_MSec:  // for the purposes of script, MSec is treated as a Scalar
+        case SkType_Point:
+        case SkType_3D_Point:
+        case SkType_Float:
+        case SkType_Array:
+scriptCommon: {
+                const char* script = valueStr.c_str();
+                success = engine.evaluateScript(&script, &scriptValue);
+                if (success == false) {
+                    maker.setScriptError(engine);
+                    return false;
+                }
+            }
+            SkASSERT(success);
+            if (scriptValue.fType == SkType_Displayable) {
+                if (type == SkType_String) {
+                    const char* charPtr = NULL;
+                    maker.findKey(scriptValue.fOperand.fDisplayable, &charPtr);
+                    scriptValue.fOperand.fString = new SkString(charPtr);
+                    scriptValue.fType = SkType_String;
+                    engine.SkScriptEngine::track(scriptValue.fOperand.fString);
+                    break;
+                }
+                SkASSERT(SkDisplayType::IsDisplayable(&maker, type));
+                if (displayable)
+                    displayable->setReference(this, scriptValue.fOperand.fDisplayable);
+                else
+                    arrayStorage->begin()[0].fDisplayable = scriptValue.fOperand.fDisplayable;
+                return true;
+            }
+            if (type != scriptValue.fType) {
+                if (scriptValue.fType == SkType_Array) {
+                    engine.forget(scriptValue.getArray());
+                    goto writeStruct; // real structs have already been written by script
+                }
+                switch (type) {
+                    case SkType_String:
+                        success = engine.convertTo(SkType_String, &scriptValue);
+                        break;
+                    case SkType_MSec:
+                    case SkType_Float:
+                        success = engine.convertTo(SkType_Float, &scriptValue);
+                        break;
+                    case SkType_Int:
+                        success = engine.convertTo(SkType_Int, &scriptValue);
+                        break;
+                    case SkType_Array:
+                        success = engine.convertTo(arrayType(), &scriptValue);
+                        // !!! incomplete; create array of appropriate type and add scriptValue to it
+                        SkASSERT(0);
+                        break;
+                    case SkType_Displayable:
+                    case SkType_Drawable:
+                        return false;   // no way to convert other types to this
+                    default:    // to avoid warnings
+                        break;
+                }
+                if (success == false)
+                    return false;
+            }
+            if (type == SkType_MSec)
+                scriptValue.fOperand.fMSec = SkScalarMulRound(scriptValue.fOperand.fScalar, 1000);
+            scriptValue.fType = type;
+        break;
+        noScriptString:
+        case SkType_DynamicString:
+            if (fType == SkType_MemberProperty && displayable) {
+                SkString string(rawValue, rawValueLen);
+                SkScriptValue scriptValue;
+                scriptValue.fOperand.fString = &string;
+                scriptValue.fType = SkType_String;
+                displayable->setProperty(propertyIndex(), scriptValue);
+            } else if (displayable) {
+                SkString* string = (SkString*) memberData(displayable);
+                string->set(rawValue, rawValueLen);
+            } else {
+                SkASSERT(arrayStorage->count() == 1);
+                arrayStorage->begin()->fString->set(rawValue, rawValueLen);
+            }
+            goto dirty;
+        case SkType_Base64: {
+            SkBase64 base64;
+            base64.decode(rawValue, rawValueLen);
+            *(SkBase64* ) untypedStorage = base64;
+            } goto dirty;
+        default:
+            SkASSERT(0);
+            break;
+    }
+//  if (SkDisplayType::IsStruct(type) == false) 
+    {
+writeStruct:
+        if (writeValue(displayable, arrayStorage, storageOffset, maxStorage, 
+                untypedStorage, outType, scriptValue)) {
+                    maker.setErrorCode(SkDisplayXMLParserError::kUnexpectedType);
+            return false;
+        }
+    }
+dirty:
+    if (displayable)
+        displayable->dirty();
+    return true;
+}
+
+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, 
+    SkScriptValue& scriptValue) const
+{
+    SkOperand* storage = untypedStorage ? (SkOperand*) untypedStorage : arrayStorage ? 
+        arrayStorage->begin() : NULL;
+    if (storage)
+        storage += storageOffset;
+    SkDisplayTypes type = getType();
+    if (fType == SkType_MemberProperty) {
+        if(displayable)
+            displayable->setProperty(propertyIndex(), scriptValue);
+        else {
+            SkASSERT(storageOffset < arrayStorage->count());
+            switch (scriptValue.fType) {
+                case SkType_Boolean:
+                case SkType_Float:
+                case SkType_Int:
+                    memcpy(&storage->fScalar, &scriptValue.fOperand.fScalar, sizeof(SkScalar));
+                    break;
+                case SkType_Array:
+                    memcpy(&storage->fScalar, scriptValue.fOperand.fArray->begin(), scriptValue.fOperand.fArray->count() * sizeof(SkScalar));
+                    break;
+                case SkType_String:
+                    storage->fString->set(*scriptValue.fOperand.fString);
+                    break;
+                default:
+                    SkASSERT(0);    // type isn't handled yet
+            }
+        }
+    } else if (fType == SkType_MemberFunction) {
+        SkASSERT(scriptValue.fType == SkType_Array);
+        if (displayable)
+            displayable->executeFunction(displayable, this, scriptValue.fOperand.fArray, NULL);
+        else {
+            int count = scriptValue.fOperand.fArray->count();
+    //      SkASSERT(maxStorage == 0 || count == maxStorage);
+            if (arrayStorage->count() == 2)
+                arrayStorage->setCount(2 * count);
+            else {
+                storageOffset *= count;
+                SkASSERT(count + storageOffset <= arrayStorage->count());
+            }
+            memcpy(&(*arrayStorage)[storageOffset], scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
+        }
+
+    } else if (fType == SkType_Array) {
+        SkTypedArray* destArray = (SkTypedArray*) (untypedStorage ? untypedStorage : arrayStorage);
+        SkASSERT(destArray);
+    //  destArray->setCount(0);
+        if (scriptValue.fType != SkType_Array) {
+            SkASSERT(type == scriptValue.fType);
+    //      SkASSERT(storageOffset + 1 <= maxStorage);
+            destArray->setCount(storageOffset + 1);
+            (*destArray)[storageOffset] = scriptValue.fOperand;
+        } else {
+            if (type == SkType_Unknown) {
+                type = scriptValue.fOperand.fArray->getType();
+                destArray->setType(type);
+            }
+            SkASSERT(type == scriptValue.fOperand.fArray->getType());
+            int count = scriptValue.fOperand.fArray->count();
+    //      SkASSERT(storageOffset + count <= maxStorage);
+            destArray->setCount(storageOffset + count);
+            memcpy(destArray->begin() + storageOffset, scriptValue.fOperand.fArray->begin(), sizeof(SkOperand) * count);
+        }
+    } else if (type == SkType_String) {
+        SkString* string = untypedStorage ? (SkString*) untypedStorage : (*arrayStorage)[storageOffset].fString;
+        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 || 
+            scriptValue.fType == SkType_Array);
+        SkASSERT(scriptValue.fType != SkType_Array || (array != NULL &&
+            array->getType() == SkType_Int));
+        int numberOfColors = scriptValue.fType == SkType_Array ? array->count() : 1;
+        int numberOfComponents = numberOfColors * 4;
+    //  SkASSERT(maxStorage == 0 || maxStorage == numberOfComponents);
+        if (maxStorage == 0)
+            arrayStorage->setCount(numberOfComponents);
+        for (int index = 0; index < numberOfColors; index++) {
+            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));
+            storage[2].fScalar = SkIntToScalar(SkColorGetG(color));
+            storage[3].fScalar = SkIntToScalar(SkColorGetB(color));
+            storage += 4;
+        }
+    } else if (SkDisplayType::IsStruct(NULL /* !!! maker*/, type)) {
+        if (scriptValue.fType != SkType_Array)
+            return true;    // error
+        SkASSERT(sizeof(SkScalar) == sizeof(SkOperand)); // !!! no 64 bit pointer support yet
+        int count = scriptValue.fOperand.fArray->count();
+        if (count > 0) {
+            SkASSERT(fCount == count);
+            memcpy(storage, scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
+        }
+    } else if (scriptValue.fType == SkType_Array) {
+        SkASSERT(scriptValue.fOperand.fArray->getType() == type);
+        SkASSERT(scriptValue.fOperand.fArray->count() == getCount());
+        memcpy(storage, scriptValue.fOperand.fArray->begin(), getCount() * sizeof(SkOperand));
+    } else {
+        memcpy(storage, &scriptValue.fOperand, sizeof(SkOperand));
+    }
+    return false;
+}
+
+
+//void SkMemberInfo::setValue(SkDisplayable* displayable, const char value[], const char name[]) const {
+//  void* valuePtr = (void*) ((char*) displayable + fOffset);
+//  switch (fType) {
+//      case SkType_Point3D: {
+//          static const char xyz[] = "x|y|z";
+//          int index = find_one(xyz, name);
+//          SkASSERT(index >= 0);
+//          valuePtr = (void*) ((char*) valuePtr + index * sizeof(SkScalar));
+//          } break;
+//      default:
+//          SkASSERT(0);
+//  }
+//  SkParse::FindScalar(value, (SkScalar*) valuePtr);
+//  displayable->dirty();
+//}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+// Find Nth memberInfo
+const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, int* index) {
+    SkASSERT(*index >= 0);
+    if (info->fType == SkType_BaseClassInfo) {
+        const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
+        const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, index);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fName);
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    if (*index >= count) {
+        *index -= count;
+        return NULL;
+    }
+    return &info[*index];
+}
+
+// Find named memberinfo
+const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, const char** matchPtr) {
+    const char* match = *matchPtr;
+    if (info->fType == SkType_BaseClassInfo) {
+        const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
+        const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, matchPtr);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fName);
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    int index = SkStrSearch(&info->fName, count, match, sizeof(*info));
+    if (index < 0 || index >= count)
+        return NULL;
+    return &info[index];
+}
+
+const SkMemberInfo* SkMemberInfo::getInherited() const {
+    return (SkMemberInfo*) fName;
+}
+
+#endif // SK_USE_CONDENSED_INFO == 0
+
+#if 0
+bool SkMemberInfo::SetValue(void* valuePtr, const char value[], SkDisplayTypes type, 
+                            int count) {
+    switch (type) {
+        case SkType_Animate:
+        case SkType_BaseBitmap: 
+        case SkType_Bitmap:
+        case SkType_Dash:
+        case SkType_Displayable:
+        case SkType_Drawable:
+        case SkType_Matrix:
+        case SkType_Path:
+        case SkType_Text:
+        case SkType_3D_Patch:
+            return false; // ref to object; caller must resolve
+        case SkType_MSec: {
+            SkParse::FindMSec(value, (SkMSec*) valuePtr);
+            } break;
+        case SkType_3D_Point:
+        case SkType_Point:
+    //  case SkType_PointArray:
+        case SkType_ScalarArray: 
+            SkParse::FindScalars(value, (SkScalar*) valuePtr, count);
+            break;
+        default:
+            SkASSERT(0);
+    }
+    return true;
+}
+#endif
+
+
diff --git a/legacy/src/animator/SkMemberInfo.h b/legacy/src/animator/SkMemberInfo.h
new file mode 100644
index 0000000..f2e690c
--- /dev/null
+++ b/legacy/src/animator/SkMemberInfo.h
@@ -0,0 +1,271 @@
+
+/*
+ * 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 SkMemberInfo_DEFINED
+#define SkMemberInfo_DEFINED
+
+#if defined SK_BUILD_CONDENSED
+    #define SK_USE_CONDENSED_INFO 0
+#endif
+
+#include "SkDisplayType.h"
+#include "SkScript.h"
+#include "SkString.h"
+#include "SkIntArray.h"
+
+class SkAnimateMaker;
+class SkDisplayable;
+class SkScriptEngine;
+
+// temporary hacks until name change is more complete
+#define SkFloat SkScalar
+#define SkInt SkS32
+
+struct SkMemberInfo {
+    //!!! alternative:
+    // if fCount == 0, record is member property
+    // then fType can be type, so caller doesn't have to check
+#if SK_USE_CONDENSED_INFO == 0
+    const char* fName;  // may be NULL for anonymous functions
+    size_t fOffset; // if negative, is index into member pointer table (for properties and functions)
+    SkDisplayTypes fType;
+    int fCount;         // for properties, actual type (count is always assumed to be 1)
+#else
+    unsigned char fName;
+    signed char fOffset;
+    unsigned char fType;
+    signed char fCount;
+#endif
+    SkDisplayTypes arrayType() const {
+        SkASSERT(fType == SkType_Array);
+        return (SkDisplayTypes) fCount;  // hack, but worth it?
+    }
+    int functionIndex() const {
+        SkASSERT(fType == SkType_MemberFunction);
+        return (signed) fOffset > 0 ? -1 + (int) fOffset : -1 - (int) fOffset;
+    }
+    bool getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const;
+    int getCount() const {
+        return fType == SkType_MemberProperty || fType == SkType_Array ||
+            fType == SkType_MemberFunction ? 1 : fCount;
+    }
+    const SkMemberInfo* getInherited() const;
+    size_t getSize(const SkDisplayable* ) const;
+    void getString(const SkDisplayable* , SkString** string) const;
+    SkDisplayTypes getType() const {
+        return fType == SkType_MemberProperty || fType == SkType_Array ||
+            fType == SkType_MemberFunction ? (SkDisplayTypes) fCount : (SkDisplayTypes) fType;
+    }
+    void getValue(const SkDisplayable* , SkOperand values[], int count) const;
+    bool isEnum() const;
+    const char* mapEnums(const char* match, int* value) const;
+    void* memberData(const SkDisplayable* displayable) const {
+        SkASSERT(fType != SkType_MemberProperty &&  fType != SkType_MemberFunction);
+        return (void*) ((const char*) displayable + fOffset);
+    }
+    int propertyIndex() const {
+        SkASSERT(fType == SkType_MemberProperty);
+        return (signed) fOffset > 0 ? -1 + (int) fOffset : -1 - (int) fOffset;
+    }
+    SkDisplayTypes propertyType() const {
+        SkASSERT(fType == SkType_MemberProperty || fType == SkType_Array);
+        return (SkDisplayTypes) fCount;  // hack, but worth it?
+    }
+    void setMemberData(SkDisplayable* displayable, const void* child, size_t size) const {
+        SkASSERT(fType != SkType_MemberProperty &&  fType != SkType_MemberFunction);
+        memcpy((char*) displayable + fOffset, child, size);
+    }
+    void setString(SkDisplayable* , SkString* ) const;
+    void setValue(SkDisplayable* , const SkOperand values[], int count) const;
+    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* , 
+        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, 
+        SkScriptValue& scriptValue) const;
+#if SK_USE_CONDENSED_INFO == 0
+    static const SkMemberInfo* Find(const SkMemberInfo [], int count, int* index);
+    static const SkMemberInfo* Find(const SkMemberInfo [], int count, const char** name);
+#else
+    static const SkMemberInfo* Find(SkDisplayTypes type, int* index);
+    static const SkMemberInfo* Find(SkDisplayTypes type, const char** name);
+#endif
+    static size_t GetSize(SkDisplayTypes type); // size of simple types only
+//  static bool SetValue(void* value, const char* name, SkDisplayTypes , int count);
+};
+
+#define SK_MEMBER(_member, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_##_type, \
+    sizeof(((BASE_CLASS*) 1)->_member) / sizeof(SkScalar) }
+
+#define SK_MEMBER_ALIAS(_member, _alias, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS, _alias), SkType_##_type, \
+    sizeof(((BASE_CLASS*) 1)->_alias) / sizeof(SkScalar) }
+
+#define SK_MEMBER_ARRAY(_member, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_Array, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_INHERITED \
+    { (const char*) INHERITED::fInfo, 0, SkType_BaseClassInfo, INHERITED::fInfoCount }
+
+// #define SK_MEMBER_KEY_TYPE(_member, _type) 
+//  {#_member, (size_t) -1, SkType_##_type, 0}
+
+#define SK_FUNCTION(_member) \
+    k_##_member##Function
+
+#define SK_PROPERTY(_member) \
+    k_##_member##Property
+
+#define SK_MEMBER_DYNAMIC_FUNCTION(_member, _type) \
+    {#_member, (size_t) (+1 + SK_FUNCTION(_member)), SkType_MemberFunction, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_DYNAMIC_PROPERTY(_member, _type) \
+    {#_member, (size_t) (1 + SK_PROPERTY(_member)), SkType_MemberProperty, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_FUNCTION(_member, _type) \
+    {#_member, (size_t) (-1 - SK_FUNCTION(_member)), SkType_MemberFunction, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_PROPERTY(_member, _type) \
+    {#_member, (size_t) (-1 - SK_PROPERTY(_member)), SkType_MemberProperty, \
+    (int) SkType_##_type }
+
+#if SK_USE_CONDENSED_INFO == 0
+
+#define DECLARE_PRIVATE_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_DRAW_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDraw##_type BASE_CLASS
+
+#define DECLARE_DISPLAY_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDisplay##_type BASE_CLASS
+
+#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[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    SkDisplayTypes fType; \
+    virtual SkDisplayTypes getType() const { return fType; } \
+    typedef _type BASE_CLASS
+
+#define DECLARE_NO_VIRTUALS_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    typedef Sk##_type BASE_CLASS
+
+#define DEFINE_GET_MEMBER(_class) \
+    const SkMemberInfo* _class::getMember(int index) { \
+        const SkMemberInfo* result = SkMemberInfo::Find(fInfo, SK_ARRAY_COUNT(fInfo), &index); \
+        return result; \
+    } \
+    const SkMemberInfo* _class::getMember(const char name[]) { \
+        const SkMemberInfo* result = SkMemberInfo::Find(fInfo, SK_ARRAY_COUNT(fInfo), &name); \
+        return result; \
+    } \
+    const int _class::fInfoCount = SK_ARRAY_COUNT(fInfo)
+
+#define DEFINE_NO_VIRTUALS_GET_MEMBER(_class) \
+    const int _class::fInfoCount = SK_ARRAY_COUNT(fInfo)
+
+#else
+
+#define DECLARE_PRIVATE_MEMBER_INFO(_type) \
+public: \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_DRAW_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDraw##_type BASE_CLASS
+
+#define DECLARE_DISPLAY_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDisplay##_type BASE_CLASS
+
+#define DECLARE_EXTRAS_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, fType, &name); } \
+    SkDisplayTypes fType; \
+    virtual SkDisplayTypes getType() const { return fType; } \
+    typedef _type BASE_CLASS
+
+#define DECLARE_NO_VIRTUALS_MEMBER_INFO(_type) \
+public: \
+    typedef Sk##_type BASE_CLASS
+
+#define DEFINE_GET_MEMBER(_class)
+#define DEFINE_NO_VIRTUALS_GET_MEMBER(_class)
+
+#endif
+
+#endif // SkMemberInfo_DEFINED
+
diff --git a/legacy/src/animator/SkOpArray.cpp b/legacy/src/animator/SkOpArray.cpp
new file mode 100644
index 0000000..1f14476
--- /dev/null
+++ b/legacy/src/animator/SkOpArray.cpp
@@ -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.
+ */
+#include "SkOpArray.h"
+
+SkOpArray::SkOpArray() : fType(SkOperand2::kNoType) {
+}
+
+SkOpArray::SkOpArray(SkOperand2::OpType type) : fType(type) {
+}
+
+bool SkOpArray::getIndex(int index, SkOperand2* operand) {
+	if (index >= count()) {
+		SkASSERT(0);
+		return false;
+	}
+	*operand = begin()[index];
+	return true;
+}
diff --git a/legacy/src/animator/SkOpArray.h b/legacy/src/animator/SkOpArray.h
new file mode 100644
index 0000000..d5b9fe7
--- /dev/null
+++ b/legacy/src/animator/SkOpArray.h
@@ -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.
+ */
+#ifndef SkOpArray_DEFINED
+#define SkOpArray_DEFINED
+
+#include "SkOperand2.h"
+#include "SkTDArray_Experimental.h"
+
+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;
+	}
+protected:
+	SkOperand2::OpType fType;
+};
+
+#endif // SkOpArray_DEFINED
diff --git a/legacy/src/animator/SkOperand.h b/legacy/src/animator/SkOperand.h
new file mode 100644
index 0000000..0bd1fa3
--- /dev/null
+++ b/legacy/src/animator/SkOperand.h
@@ -0,0 +1,46 @@
+
+/*
+ * 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 SkOperand_DEFINED
+#define SkOperand_DEFINED
+
+#include "SkDisplayType.h"
+
+class SkTypedArray;
+class SkDisplayable;
+class SkDrawable;
+class SkString;
+
+union SkOperand {
+//  SkOperand() {}
+//  SkOperand(SkScalar scalar) : fScalar(scalar) {}
+    SkTypedArray* fArray;
+    SkDisplayable* fDisplayable;
+    SkDrawable* fDrawable;
+    void* fObject;
+    int32_t fS32;
+    SkMSec fMSec;
+    SkScalar fScalar;
+    SkString* fString;
+};
+
+struct SkScriptValue {
+    SkOperand fOperand;
+    SkDisplayTypes fType;
+    SkTypedArray* getArray() { SkASSERT(fType == SkType_Array); return fOperand.fArray; }
+    SkDisplayable* getDisplayable() { SkASSERT(fType == SkType_Displayable); return fOperand.fDisplayable; }
+    SkDrawable* getDrawable() { SkASSERT(fType == SkType_Drawable); return fOperand.fDrawable; }
+    int32_t getS32(SkAnimateMaker* maker) { SkASSERT(fType == SkType_Int || fType == SkType_Boolean ||
+        SkDisplayType::IsEnum(maker, fType)); return fOperand.fS32; }
+    SkMSec getMSec() { SkASSERT(fType == SkType_MSec); return fOperand.fMSec; }
+    SkScalar getScalar() { SkASSERT(fType == SkType_Float); return fOperand.fScalar; }
+    SkString* getString() { SkASSERT(fType == SkType_String); return fOperand.fString; }
+};
+
+#endif // SkOperand_DEFINED
diff --git a/legacy/src/animator/SkOperand2.h b/legacy/src/animator/SkOperand2.h
new file mode 100644
index 0000000..4f09a01
--- /dev/null
+++ b/legacy/src/animator/SkOperand2.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 SkOperand2_DEFINED
+#define SkOperand2_DEFINED
+
+#include "SkScalar.h"
+
+class SkOpArray;
+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;
+};
+
+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; }
+        bool isConstant() const { return fIsConstant == kConstant; }
+};
+
+#endif // SkOperand2_DEFINED
diff --git a/legacy/src/animator/SkOperandInterpolator.h b/legacy/src/animator/SkOperandInterpolator.h
new file mode 100644
index 0000000..82fa272
--- /dev/null
+++ b/legacy/src/animator/SkOperandInterpolator.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 SkOperandInterpolator_DEFINED
+#define SkOperandInterpolator_DEFINED
+
+#include "SkDisplayType.h"
+#include "SkInterpolator.h"
+#include "SkOperand.h"
+
+class SkOperandInterpolator : public SkInterpolatorBase {
+public:
+    SkOperandInterpolator();
+    SkOperandInterpolator(int elemCount, int frameCount, SkDisplayTypes type);
+    SkOperand* getValues() { return fValues; }
+    int getValuesCount() { return fFrameCount * fElemCount; }
+    void    reset(int elemCount, int frameCount, SkDisplayTypes type);
+
+    /** Add or replace a key frame, copying the values[] data into the interpolator.
+        @param index    The index of this frame (frames must be ordered by time)
+        @param time The millisecond time for this frame
+        @param values   The array of values [elemCount] for this frame. The data is copied
+                        into the interpolator.
+        @param blend    A positive scalar specifying how to blend between this and the next key frame.
+                        [0...1) is a cubic lag/log/lag blend (slow to change at the beginning and end)
+                        1 is a linear blend (default)
+                        (1...inf) is a cubic log/lag/log blend (fast to change at the beginning and end)
+    */
+    bool    setKeyFrame(int index, SkMSec time, const SkOperand values[], SkScalar blend = SK_Scalar1);
+    Result timeToValues(SkMSec time, SkOperand values[]) const;
+    SkDEBUGCODE(static void UnitTest();)
+private:
+    SkDisplayTypes fType;
+    SkOperand* fValues;     // pointer into fStorage
+#ifdef SK_DEBUG
+    SkOperand(* fValuesArray)[10];
+#endif
+    typedef SkInterpolatorBase INHERITED;
+};
+
+#endif // SkOperandInterpolator_DEFINED
+
diff --git a/legacy/src/animator/SkOperandIterpolator.cpp b/legacy/src/animator/SkOperandIterpolator.cpp
new file mode 100644
index 0000000..dcc454d
--- /dev/null
+++ b/legacy/src/animator/SkOperandIterpolator.cpp
@@ -0,0 +1,151 @@
+
+/*
+ * 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 "SkOperandInterpolator.h"
+#include "SkScript.h"
+
+SkOperandInterpolator::SkOperandInterpolator() {
+    INHERITED::reset(0, 0);
+    fType = SkType_Unknown;
+}
+
+SkOperandInterpolator::SkOperandInterpolator(int elemCount, int frameCount, 
+                                             SkDisplayTypes type)
+{
+    this->reset(elemCount, frameCount, type);
+}
+
+void SkOperandInterpolator::reset(int elemCount, int frameCount, SkDisplayTypes type)
+{
+//  SkASSERT(type == SkType_String || type == SkType_Float || type == SkType_Int ||
+//      type == SkType_Displayable || type == SkType_Drawable);
+    INHERITED::reset(elemCount, frameCount);
+    fType = type;
+    fStorage = sk_malloc_throw((sizeof(SkOperand) * elemCount + sizeof(SkTimeCode)) * frameCount);
+    fTimes = (SkTimeCode*) fStorage;
+    fValues = (SkOperand*) ((char*) fStorage + sizeof(SkTimeCode) * frameCount);
+#ifdef SK_DEBUG
+    fTimesArray = (SkTimeCode(*)[10]) fTimes;
+    fValuesArray = (SkOperand(*)[10]) fValues;
+#endif
+}
+
+bool SkOperandInterpolator::setKeyFrame(int index, SkMSec time, const SkOperand values[], SkScalar blend)
+{
+    SkASSERT(values != NULL);
+    blend = SkScalarPin(blend, 0, SK_Scalar1);
+
+    bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, sizeof(SkTimeCode));
+    SkASSERT(success);
+    if (success) {
+        SkTimeCode* timeCode = &fTimes[index];
+        timeCode->fTime = time;
+        timeCode->fBlend[0] = SK_Scalar1 - blend;
+        timeCode->fBlend[1] = 0;
+        timeCode->fBlend[2] = 0;
+        timeCode->fBlend[3] = SK_Scalar1 - blend;
+        SkOperand* dst = &fValues[fElemCount * index];
+        memcpy(dst, values, fElemCount * sizeof(SkOperand));
+    }
+    return success;
+}
+
+SkInterpolatorBase::Result SkOperandInterpolator::timeToValues(SkMSec time, SkOperand values[]) const
+{
+    SkScalar T;
+    int index;
+    SkBool exact;
+    Result result = timeToT(time, &T, &index, &exact);
+    if (values)
+    {
+        const SkOperand* nextSrc = &fValues[index * fElemCount];
+
+        if (exact)
+            memcpy(values, nextSrc, fElemCount * sizeof(SkScalar));
+        else
+        {
+            SkASSERT(index > 0);
+
+            const SkOperand* prevSrc = nextSrc - fElemCount;
+
+            if (fType == SkType_Float || fType == SkType_3D_Point) {
+                for (int i = fElemCount - 1; i >= 0; --i)
+                    values[i].fScalar = SkScalarInterp(prevSrc[i].fScalar, nextSrc[i].fScalar, T);
+            } else if (fType == SkType_Int || fType == SkType_MSec) {
+                for (int i = fElemCount - 1; i >= 0; --i) {
+                    int32_t a = prevSrc[i].fS32;
+                    int32_t b = nextSrc[i].fS32;
+                    values[i].fS32 = a + SkScalarRound((b - a) * T);
+                }
+            } else
+                memcpy(values, prevSrc, sizeof(SkOperand) * fElemCount);
+        }
+    }
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#ifdef SK_SUPPORT_UNITTEST
+    static SkOperand* iset(SkOperand array[3], int a, int b, int c)
+    {
+        array[0].fScalar = SkIntToScalar(a);
+        array[1].fScalar = SkIntToScalar(b);
+        array[2].fScalar = SkIntToScalar(c);
+        return array;
+    }
+#endif
+
+void SkOperandInterpolator::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkOperandInterpolator   inter(3, 2, SkType_Float);
+    SkOperand       v1[3], v2[3], v[3], vv[3];
+    Result          result;
+
+    inter.setKeyFrame(0, 100, iset(v1, 10, 20, 30), 0);
+    inter.setKeyFrame(1, 200, iset(v2, 110, 220, 330));
+
+    result = inter.timeToValues(0, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(99, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(100, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(200, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(201, v);
+    SkASSERT(result == kFreezeEnd_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(150, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, iset(vv, 60, 120, 180), sizeof(v)) == 0);
+
+    result = inter.timeToValues(125, v);
+    SkASSERT(result == kNormal_Result);
+    result = inter.timeToValues(175, v);
+    SkASSERT(result == kNormal_Result);
+#endif
+}
+
+#endif
+
+
diff --git a/legacy/src/animator/SkPaintParts.cpp b/legacy/src/animator/SkPaintParts.cpp
new file mode 100644
index 0000000..2959238
--- /dev/null
+++ b/legacy/src/animator/SkPaintParts.cpp
@@ -0,0 +1,103 @@
+
+/*
+ * 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 "SkPaintParts.h"
+#include "SkDrawPaint.h"
+#ifdef SK_DUMP_ENABLED
+#include "SkDisplayList.h"
+#include "SkDump.h"
+#endif
+
+SkPaintPart::SkPaintPart() : fPaint(NULL) {
+}
+
+SkDisplayable* SkPaintPart::getParent() const {
+    return fPaint;
+}
+
+bool SkPaintPart::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->isPaint() == false)
+        return true;
+    fPaint = (SkDrawPaint*) parent;
+    return false;
+}
+
+
+// SkDrawMaskFilter
+bool SkDrawMaskFilter::add() {
+    if (fPaint->maskFilter != (SkDrawMaskFilter*) -1)
+        return true;
+    fPaint->maskFilter = this;
+    fPaint->fOwnsMaskFilter = true;
+    return false;
+}
+
+SkMaskFilter* SkDrawMaskFilter::getMaskFilter() {
+    return NULL;
+}
+
+
+// SkDrawPathEffect
+bool SkDrawPathEffect::add() {
+    if (fPaint->isPaint()) {
+        if (fPaint->pathEffect != (SkDrawPathEffect*) -1)
+            return true;
+        fPaint->pathEffect = this;
+        fPaint->fOwnsPathEffect = true;
+        return false;
+    }
+    fPaint->add(NULL, this);
+    return false;
+}
+
+SkPathEffect* SkDrawPathEffect::getPathEffect() {
+    return NULL;
+}
+
+
+// SkDrawShader
+SkShader* SkDrawShader::getShader() {
+    return NULL;
+}
+
+
+// Typeface
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawTypeface::fInfo[] = {
+    SK_MEMBER(fontName, String),
+    SK_MEMBER(style, FontStyle)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawTypeface);
+
+SkDrawTypeface::SkDrawTypeface() : style (SkTypeface::kNormal){
+}
+
+bool SkDrawTypeface::add() {
+    if (fPaint->typeface != (SkDrawTypeface*) -1)
+        return true;
+    fPaint->typeface = this;
+    fPaint->fOwnsTypeface = true;
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawTypeface::dump(SkAnimateMaker* maker) {
+    SkDebugf("%*s<typeface fontName=\"%s\" ", SkDisplayList::fIndent, "", fontName.c_str());
+    SkString string;
+    SkDump::GetEnumString(SkType_FontStyle, style, &string);
+    SkDebugf("style=\"%s\" />\n", string.c_str());
+}
+#endif
+
+
diff --git a/legacy/src/animator/SkPaintParts.h b/legacy/src/animator/SkPaintParts.h
new file mode 100644
index 0000000..964bc35
--- /dev/null
+++ b/legacy/src/animator/SkPaintParts.h
@@ -0,0 +1,75 @@
+
+/*
+ * 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 SkPaintParts_DEFINED
+#define SkPaintParts_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+class SkDrawPaint;
+class SkDrawMatrix;
+
+class SkPaintPart : public SkDisplayable {
+public:
+    SkPaintPart();
+    virtual bool add() = 0;
+    virtual SkDisplayable* getParent() const;
+    virtual bool setParent(SkDisplayable* parent);
+#ifdef SK_DEBUG
+    virtual bool isPaintPart() const { return true; }
+#endif
+protected:
+    SkDrawPaint* fPaint;
+};
+
+class SkDrawMaskFilter : public SkPaintPart {
+    DECLARE_EMPTY_MEMBER_INFO(MaskFilter);
+    virtual SkMaskFilter* getMaskFilter();
+protected:
+    virtual bool add();
+};
+
+class SkDrawPathEffect : public SkPaintPart {
+    DECLARE_EMPTY_MEMBER_INFO(PathEffect);
+    virtual SkPathEffect* getPathEffect();
+protected:
+    virtual bool add();
+};
+
+class SkDrawShader : public SkPaintPart {
+    DECLARE_DRAW_MEMBER_INFO(Shader);
+    SkDrawShader();
+    virtual SkShader* getShader();
+protected:
+    virtual bool add();
+    void addPostlude(SkShader* shader);
+    SkDrawMatrix* matrix;
+    int /*SkShader::TileMode*/ tileMode;
+};
+
+class SkDrawTypeface  : public SkPaintPart {
+    DECLARE_DRAW_MEMBER_INFO(Typeface);
+    SkDrawTypeface();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker *);
+#endif
+    SkTypeface* getTypeface() {
+        return SkTypeface::CreateFromName(fontName.c_str(), style); }
+protected:
+    virtual bool add();
+    SkString fontName;
+    SkTypeface::Style style;
+};
+
+#endif // SkPaintParts_DEFINED
diff --git a/legacy/src/animator/SkParseSVGPath.cpp b/legacy/src/animator/SkParseSVGPath.cpp
new file mode 100644
index 0000000..1b375aa
--- /dev/null
+++ b/legacy/src/animator/SkParseSVGPath.cpp
@@ -0,0 +1,235 @@
+
+/*
+ * 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 <ctype.h>
+#include "SkDrawPath.h"
+#include "SkParse.h"
+#include "SkPoint.h"
+#include "SkUtils.h"
+#define QUADRATIC_APPROXIMATION 1
+
+#if QUADRATIC_APPROXIMATION
+////////////////////////////////////////////////////////////////////////////////////
+//functions to approximate a cubic using two quadratics
+
+//      midPt sets the first argument to be the midpoint of the other two
+//      it is used by quadApprox
+static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b)
+{
+    dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY));
+}
+//      quadApprox - makes an approximation, which we hope is faster
+static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
+{
+    //divide the cubic up into two cubics, then convert them into quadratics
+    //define our points
+    SkPoint c,j,k,l,m,n,o,p,q, mid;
+    fPath.getLastPt(&c);
+    midPt(j, p0, c);
+    midPt(k, p0, p1);
+    midPt(l, p1, p2);
+    midPt(o, j, k);
+    midPt(p, k, l);
+    midPt(q, o, p);
+    //compute the first half
+    m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY));
+    n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY));
+    midPt(mid,m,n);
+    fPath.quadTo(mid,q);
+    c = q;
+    //compute the second half
+    m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY));
+    n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY));
+    midPt(mid,m,n);
+    fPath.quadTo(mid,p2);
+}
+#endif
+
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static inline bool is_digit(int c)
+{
+    return is_between(c, '0', '9');
+}
+
+static inline bool is_sep(int c)
+{
+    return is_ws(c) || c == ',';
+}
+
+static const char* skip_ws(const char str[])
+{
+    SkASSERT(str);
+    while (is_ws(*str))
+        str++;
+    return str;
+}
+
+static const char* skip_sep(const char str[])
+{
+    SkASSERT(str);
+    while (is_sep(*str))
+        str++;
+    return str;
+}
+
+static const char* find_points(const char str[], SkPoint value[], int count,
+     bool isRelative, SkPoint* relative)
+{
+    str = SkParse::FindScalars(str, &value[0].fX, count * 2);
+    if (isRelative) {
+        for (int index = 0; index < count; index++) {
+            value[index].fX += relative->fX;
+            value[index].fY += relative->fY;
+        }
+    }
+    return str;
+}
+
+static const char* find_scalar(const char str[], SkScalar* value, 
+    bool isRelative, SkScalar relative)
+{
+    str = SkParse::FindScalar(str, value);
+    if (isRelative)
+        *value += relative;
+    return str;
+}
+
+void SkDrawPath::parseSVG() {
+    fPath.reset();
+    const char* data = d.c_str();
+    SkPoint f = {0, 0};
+    SkPoint c = {0, 0};
+    SkPoint lastc = {0, 0};
+    SkPoint points[3];
+    char op = '\0';
+    char previousOp = '\0';
+    bool relative = false;
+    do {
+        data = skip_ws(data);
+        if (data[0] == '\0')
+            break;
+        char ch = data[0];
+        if (is_digit(ch) || ch == '-' || ch == '+') {
+            if (op == '\0')
+                return;
+        }
+        else {
+            op = ch;
+            relative = false;
+            if (islower(op)) {
+                op = (char) toupper(op);
+                relative = true;
+            }
+            data++;
+            data = skip_sep(data);
+        }
+        switch (op) {
+            case 'M':
+                data = find_points(data, points, 1, relative, &c);
+                fPath.moveTo(points[0]);
+                op = 'L';
+                c = points[0];
+                break;
+            case 'L': 
+                data = find_points(data, points, 1, relative, &c);
+                fPath.lineTo(points[0]);
+                c = points[0];
+                break;
+            case 'H': {
+                SkScalar x;
+                data = find_scalar(data, &x, relative, c.fX);
+                fPath.lineTo(x, c.fY);
+                c.fX = x;
+            }
+                break;
+            case 'V': {
+                SkScalar y;
+                data = find_scalar(data, &y, relative, c.fY);
+                fPath.lineTo(c.fX, y);
+                c.fY = y;
+            }
+                break;
+            case 'C': 
+                data = find_points(data, points, 3, relative, &c);
+                goto cubicCommon;
+            case 'S': 
+                data = find_points(data, &points[1], 2, relative, &c);
+                points[0] = c;
+                if (previousOp == 'C' || previousOp == 'S') {
+                    points[0].fX -= lastc.fX - c.fX;
+                    points[0].fY -= lastc.fY - c.fY;
+                }
+            cubicCommon:
+    //          if (data[0] == '\0')
+    //              return;
+#if QUADRATIC_APPROXIMATION
+                    quadApprox(fPath, points[0], points[1], points[2]);
+#else   //this way just does a boring, slow old cubic
+                    fPath.cubicTo(points[0], points[1], points[2]);
+#endif
+        //if we are using the quadApprox, lastc is what it would have been if we had used
+        //cubicTo
+                    lastc = points[1];
+                    c = points[2];
+                break;
+            case 'Q':  // Quadratic Bezier Curve
+                data = find_points(data, points, 2, relative, &c);
+                goto quadraticCommon;
+            case 'T':
+                data = find_points(data, &points[1], 1, relative, &c);
+                points[0] = points[1];
+                if (previousOp == 'Q' || previousOp == 'T') {
+                    points[0].fX = c.fX * 2 - lastc.fX;
+                    points[0].fY = c.fY * 2 - lastc.fY;
+                }
+            quadraticCommon:
+                fPath.quadTo(points[0], points[1]);
+                lastc = points[0];
+                c = points[1];
+                break;
+            case 'Z':
+                fPath.close();
+#if 0   // !!! still a bug?
+                if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
+                    c.fX -= SkScalar.Epsilon;   // !!! enough?
+                    fPath.moveTo(c);
+                    fPath.lineTo(f);
+                    fPath.close();
+                }
+#endif
+                c = f;
+                op = '\0';
+                break;
+            case '~': {
+                SkPoint args[2];
+                data = find_points(data, args, 2, false, NULL);
+                fPath.moveTo(args[0].fX, args[0].fY);
+                fPath.lineTo(args[1].fX, args[1].fY);
+            }
+                break;
+            default:
+                SkASSERT(0);
+                return;
+        }
+        if (previousOp == 0)
+            f = c;
+        previousOp = op;
+    } while (data[0] > 0);
+}
+
diff --git a/legacy/src/animator/SkPathParts.cpp b/legacy/src/animator/SkPathParts.cpp
new file mode 100644
index 0000000..5af8150
--- /dev/null
+++ b/legacy/src/animator/SkPathParts.cpp
@@ -0,0 +1,320 @@
+
+/*
+ * 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 "SkPathParts.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawRectangle.h"
+#include "SkDrawPath.h"
+
+SkPathPart::SkPathPart() : fPath(NULL) {
+}
+
+void SkPathPart::dirty() { 
+    fPath->dirty(); 
+}
+
+SkDisplayable* SkPathPart::getParent() const {
+    return fPath;
+}
+
+bool SkPathPart::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->isPath() == false)
+        return true;
+    fPath = (SkDrawPath*) parent;
+    return false;
+}
+
+// MoveTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkMoveTo::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkMoveTo);
+
+SkMoveTo::SkMoveTo() : x(0), y(0) {
+}
+
+bool SkMoveTo::add() {
+    fPath->fPath.moveTo(x, y);
+    return false;
+}
+
+
+// RMoveTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRMoveTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRMoveTo);
+
+bool SkRMoveTo::add() {
+    fPath->fPath.rMoveTo(x, y);
+    return false;
+}
+
+
+// LineTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkLineTo::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkLineTo);
+
+SkLineTo::SkLineTo() : x(0), y(0) {
+}
+
+bool SkLineTo::add() {
+    fPath->fPath.lineTo(x, y);  
+    return false;
+}
+
+
+// RLineTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRLineTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRLineTo);
+
+bool SkRLineTo::add() {
+    fPath->fPath.rLineTo(x, y); 
+    return false;
+}
+
+
+// QuadTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkQuadTo::fInfo[] = {
+    SK_MEMBER(x1, Float),
+    SK_MEMBER(x2, Float),
+    SK_MEMBER(y1, Float),
+    SK_MEMBER(y2, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkQuadTo);
+
+SkQuadTo::SkQuadTo() : x1(0), y1(0), x2(0), y2(0) {
+}
+
+bool SkQuadTo::add() {
+    fPath->fPath.quadTo(x1, y1, x2, y2);
+    return false;
+}
+
+
+// RQuadTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRQuadTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRQuadTo);
+
+bool SkRQuadTo::add() {
+    fPath->fPath.rQuadTo(x1, y1, x2, y2);
+    return false;
+}
+
+
+// CubicTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkCubicTo::fInfo[] = {
+    SK_MEMBER(x1, Float),
+    SK_MEMBER(x2, Float),
+    SK_MEMBER(x3, Float),
+    SK_MEMBER(y1, Float),
+    SK_MEMBER(y2, Float),
+    SK_MEMBER(y3, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkCubicTo);
+
+SkCubicTo::SkCubicTo() : x1(0), y1(0), x2(0), y2(0), x3(0), y3(0) {
+}
+
+bool SkCubicTo::add() {
+    fPath->fPath.cubicTo(x1, y1, x2, y2, x3, y3);
+    return false;
+}
+
+
+// RCubicTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRCubicTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRCubicTo);
+
+bool SkRCubicTo::add() {
+    fPath->fPath.rCubicTo(x1, y1, x2, y2, x3, y3);
+    return false;
+}
+
+
+// SkClose
+bool SkClose::add() {
+    fPath->fPath.close();
+    return false;
+}
+
+
+// SkAddGeom
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddGeom::fInfo[] = {
+    SK_MEMBER(direction, PathDirection)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddGeom);
+
+SkAddGeom::SkAddGeom() : direction(SkPath::kCCW_Direction) {
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddRect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ALIAS(bottom, fRect.fBottom, Float),
+    SK_MEMBER_ALIAS(left, fRect.fLeft, Float),
+    SK_MEMBER_ALIAS(right, fRect.fRight, Float),
+    SK_MEMBER_ALIAS(top, fRect.fTop, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddRect);
+
+SkAddRect::SkAddRect() { 
+    fRect.setEmpty(); 
+}
+
+bool SkAddRect::add() {
+    fPath->fPath.addRect(fRect, (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddOval::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddOval);
+
+bool SkAddOval::add() {
+    fPath->fPath.addOval(fRect,  (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddCircle::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(radius, Float),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddCircle);
+
+SkAddCircle::SkAddCircle() : radius(0), x(0), y(0) {
+}
+
+bool SkAddCircle::add() {
+    fPath->fPath.addCircle(x, y, radius,  (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddRoundRect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(rx, Float),
+    SK_MEMBER(ry, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddRoundRect);
+
+SkAddRoundRect::SkAddRoundRect() : rx(0), ry(0) {
+}
+
+bool SkAddRoundRect::add() {
+    fPath->fPath.addRoundRect(fRect, rx, ry,  (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddPath::fInfo[] = {
+    SK_MEMBER(matrix, Matrix),
+    SK_MEMBER(path, Path)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddPath);
+
+SkAddPath::SkAddPath() : matrix(NULL), path(NULL) {
+}
+
+bool SkAddPath::add() {
+    SkASSERT (path != NULL); 
+    if (matrix)
+        fPath->fPath.addPath(path->fPath, matrix->getMatrix());
+    else
+        fPath->fPath.addPath(path->fPath);
+    return false;
+}
+
+
diff --git a/legacy/src/animator/SkPathParts.h b/legacy/src/animator/SkPathParts.h
new file mode 100644
index 0000000..cc81cdd
--- /dev/null
+++ b/legacy/src/animator/SkPathParts.h
@@ -0,0 +1,165 @@
+
+/*
+ * 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 SkPathParts_DEFINED
+#define SkPathParts_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkPath.h"
+
+class SkDrawPath;
+class SkDrawMatrix;
+
+class SkPathPart : public SkDisplayable {
+public:
+    SkPathPart();
+    virtual bool add() = 0;
+    virtual void dirty();
+    virtual SkDisplayable* getParent() const;
+    virtual bool setParent(SkDisplayable* parent);
+#ifdef SK_DEBUG
+    virtual bool isPathPart() const { return true; }
+#endif
+protected:
+    SkDrawPath* fPath;
+};
+
+class SkMoveTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(MoveTo);
+    SkMoveTo();
+    virtual bool add();
+protected:
+    SkScalar x;
+    SkScalar y;
+};
+
+class SkRMoveTo : public SkMoveTo {
+    DECLARE_MEMBER_INFO(RMoveTo);
+    virtual bool add();
+private:
+    typedef SkMoveTo INHERITED;
+};
+
+class SkLineTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(LineTo);
+    SkLineTo();
+    virtual bool add();
+protected:
+    SkScalar x;
+    SkScalar y;
+};
+
+class SkRLineTo : public SkLineTo {
+    DECLARE_MEMBER_INFO(RLineTo);
+    virtual bool add();
+private:
+    typedef SkLineTo INHERITED;
+};
+
+class SkQuadTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(QuadTo);
+    SkQuadTo();
+    virtual bool add();
+protected:
+    SkScalar x1;
+    SkScalar y1;
+    SkScalar x2;
+    SkScalar y2;
+};
+
+class SkRQuadTo : public SkQuadTo {
+    DECLARE_MEMBER_INFO(RQuadTo);
+    virtual bool add();
+private:
+    typedef SkQuadTo INHERITED;
+};
+
+class SkCubicTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(CubicTo);
+    SkCubicTo();
+    virtual bool add();
+protected:
+    SkScalar x1;
+    SkScalar y1;
+    SkScalar x2;
+    SkScalar y2;
+    SkScalar x3;
+    SkScalar y3;
+};
+
+class SkRCubicTo : public SkCubicTo {
+    DECLARE_MEMBER_INFO(RCubicTo);
+    virtual bool add();
+private:
+    typedef SkCubicTo INHERITED;
+};
+
+class SkClose : public SkPathPart {
+    DECLARE_EMPTY_MEMBER_INFO(Close);
+    virtual bool add();
+};
+
+class SkAddGeom : public SkPathPart {
+    DECLARE_PRIVATE_MEMBER_INFO(AddGeom);
+    SkAddGeom();
+protected:
+    int /*SkPath::Direction*/ direction;
+};
+
+class SkAddRect : public SkAddGeom {
+    DECLARE_MEMBER_INFO(AddRect);
+    SkAddRect();
+    virtual bool add();
+protected:
+    SkRect fRect;
+private:
+    typedef SkAddGeom INHERITED;
+};
+
+class SkAddOval : public SkAddRect {
+    DECLARE_MEMBER_INFO(AddOval);
+    virtual bool add();
+private:
+    typedef SkAddRect INHERITED;
+};
+
+class SkAddCircle : public SkAddGeom {
+    DECLARE_MEMBER_INFO(AddCircle);
+    SkAddCircle();
+    virtual bool add();
+private:
+    SkScalar radius;
+    SkScalar x;
+    SkScalar y;
+    typedef SkAddGeom INHERITED;
+};
+
+class SkAddRoundRect : public SkAddRect {
+    DECLARE_MEMBER_INFO(AddRoundRect);
+    SkAddRoundRect();
+    virtual bool add();
+private:
+    SkScalar rx;
+    SkScalar ry;
+    typedef SkAddRect INHERITED;
+};
+
+class SkAddPath : public SkPathPart {
+    DECLARE_MEMBER_INFO(AddPath);
+    SkAddPath();
+    virtual bool add();
+private:
+    typedef SkPathPart INHERITED;
+    SkDrawMatrix* matrix;
+    SkDrawPath* path;
+};
+
+#endif // SkPathParts_DEFINED
+
diff --git a/legacy/src/animator/SkPostParts.cpp b/legacy/src/animator/SkPostParts.cpp
new file mode 100644
index 0000000..4b776a8
--- /dev/null
+++ b/legacy/src/animator/SkPostParts.cpp
@@ -0,0 +1,57 @@
+
+/*
+ * 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 "SkPostParts.h"
+#include "SkDisplayPost.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDataInput::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDataInput);
+
+SkDataInput::SkDataInput() : fParent(NULL) {}
+
+bool SkDataInput::add() {
+    SkASSERT(name.size() > 0);
+    const char* dataName = name.c_str();
+    if (fInt != (int) SK_NaN32)
+        fParent->fEvent.setS32(dataName, fInt);
+    else if (SkScalarIsNaN(fFloat) == false)
+        fParent->fEvent.setScalar(dataName, fFloat);
+    else if (string.size() > 0) 
+        fParent->fEvent.setString(dataName, string);
+//  else
+//      SkASSERT(0);
+    return false;
+}
+
+void SkDataInput::dirty() {
+    fParent->dirty();
+}
+
+SkDisplayable* SkDataInput::getParent() const {
+    return fParent;
+}
+
+bool SkDataInput::setParent(SkDisplayable* displayable) {
+    if (displayable->isPost() == false)
+        return true;
+    fParent = (SkPost*) displayable;
+    return false;
+}
+
+void SkDataInput::onEndElement(SkAnimateMaker&) {
+    add();
+}
+
diff --git a/legacy/src/animator/SkPostParts.h b/legacy/src/animator/SkPostParts.h
new file mode 100644
index 0000000..4101b2b
--- /dev/null
+++ b/legacy/src/animator/SkPostParts.h
@@ -0,0 +1,31 @@
+
+/*
+ * 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 SkPostParts_DEFINED
+#define SkPostParts_DEFINED
+
+#include "SkDisplayInput.h"
+
+class SkPost;
+
+class SkDataInput: public SkInput {
+    DECLARE_MEMBER_INFO(DataInput);
+    SkDataInput();
+    bool add();
+    virtual void dirty();
+    virtual SkDisplayable* getParent() const;
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual bool setParent(SkDisplayable* );
+protected:
+    SkPost* fParent;
+    typedef SkInput INHERITED;
+    friend class SkPost;
+};
+
+#endif // SkPostParts_DEFINED
diff --git a/legacy/src/animator/SkScript.cpp b/legacy/src/animator/SkScript.cpp
new file mode 100644
index 0000000..aa06169
--- /dev/null
+++ b/legacy/src/animator/SkScript.cpp
@@ -0,0 +1,1908 @@
+
+/*
+ * 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 "SkScript.h"
+#include "SkMath.h"
+#include "SkParse.h"
+#include "SkString.h"
+#include "SkTypedArray.h"
+
+/* 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");  
+    finish support for typed arrays
+        ? allow indexing arrays by string
+            this could map to the 'name' attribute of a given child of an array
+        ? allow multiple types in the array
+    remove SkDisplayType.h  // from SkOperand.h
+    merge type and operand arrays into scriptvalue array
+*/
+
+#ifdef SK_DEBUG
+static const char* errorStrings[] = {
+        "array index of out bounds", // kArrayIndexOutOfBounds
+        "could not find reference id", // kCouldNotFindReferencedID
+        "dot operator expects object", // kDotOperatorExpectsObject
+        "error in array index", // kErrorInArrrayIndex
+        "error in function parameters", // kErrorInFunctionParameters
+        "expected array", // kExpectedArray
+        "expected boolean expression", // kExpectedBooleanExpression
+        "expected field name", // kExpectedFieldName
+        "expected hex", // kExpectedHex
+        "expected int for condition operator", // kExpectedIntForConditionOperator
+        "expected number", // kExpectedNumber
+        "expected number for array index", // kExpectedNumberForArrayIndex
+        "expected operator", // kExpectedOperator
+        "expected token", // kExpectedToken
+        "expected token before dot operator", // kExpectedTokenBeforeDotOperator
+        "expected value", // kExpectedValue
+        "handle member failed", // kHandleMemberFailed
+        "handle member function failed", // kHandleMemberFunctionFailed
+        "handle unbox failed", // kHandleUnboxFailed
+        "index out of range", // kIndexOutOfRange
+        "mismatched array brace", // kMismatchedArrayBrace
+        "mismatched brackets", // kMismatchedBrackets
+        "no function handler found", // kNoFunctionHandlerFound
+        "premature end", // kPrematureEnd
+        "too many parameters", // kTooManyParameters
+        "type conversion failed", // kTypeConversionFailed
+        "unterminated string" // kUnterminatedString
+};
+#endif
+
+const SkScriptEngine::SkOperatorAttributes SkScriptEngine::gOpAttributes[] = {
+    { kNoType, kNoType, kNoBias }, //   kUnassigned,
+    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsString }, // kAdd
+    // kAddInt = kAdd,
+    { kNoType, kNoType, kNoBias },  // kAddScalar,
+    { kNoType, kNoType, kNoBias },  // kAddString,
+    { kNoType, kNoType, kNoBias },  // kArrayOp,
+    { kInt, kInt, kNoBias }, // kBitAnd
+    { kNoType, kInt, kNoBias }, // kBitNot
+    { kInt, kInt, kNoBias }, // kBitOr
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kDivide
+    // kDivideInt = kDivide
+    { kNoType, kNoType, kNoBias },  // kDivideScalar
+    { kNoType, kNoType, kNoBias },  // kElse
+    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kEqual
+    // kEqualInt = kEqual
+    { kNoType, kNoType, kNoBias },  // kEqualScalar
+    { kNoType, kNoType, kNoBias },  // kEqualString
+    { kInt, kNoType, kNoBias },     // kFlipOps
+    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kGreaterEqual
+    // kGreaterEqualInt = kGreaterEqual
+    { kNoType, kNoType, kNoBias },  // kGreaterEqualScalar
+    { kNoType, kNoType, kNoBias },  // kGreaterEqualString
+    { kNoType, kNoType, kNoBias },  // kIf
+    { kNoType, kInt, kNoBias }, // kLogicalAnd  (really, ToBool)
+    { kNoType, kInt, kNoBias }, // kLogicalNot
+    { kInt, kInt, kNoBias }, // kLogicalOr
+    { kNoType, SkOpType(kInt | kScalar), kNoBias }, // kMinus
+    // kMinusInt = kMinus
+    { kNoType, kNoType, kNoBias },  // kMinusScalar
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kModulo
+    // kModuloInt = kModulo
+    { kNoType, kNoType, kNoBias },  // kModuloScalar
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kMultiply
+    // kMultiplyInt = kMultiply
+    { kNoType, kNoType, kNoBias },  // kMultiplyScalar
+    { kNoType, kNoType, kNoBias },  // kParen
+    { kInt, kInt, kNoBias }, // kShiftLeft
+    { kInt, kInt, kNoBias }, // kShiftRight
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kSubtract
+    // kSubtractInt = kSubtract
+    { kNoType, kNoType, kNoBias },  // kSubtractScalar
+    { kInt, kInt, kNoBias } // kXor
+};
+
+// Note that the real precedence for () [] is '2'
+// but here, precedence means 'while an equal or smaller precedence than the current operator
+// is on the stack, process it. This allows 3+5*2 to defer the add until after the multiply
+// is preformed, since the add precedence is not smaller than multiply.
+// But, (3*4 does not process the '(', since brackets are greater than all other precedences
+#define kBracketPrecedence 16
+#define kIfElsePrecedence 15
+
+const signed char SkScriptEngine::gPrecedence[] = {
+        -1, //  kUnassigned,
+        6, // kAdd,
+        // kAddInt = kAdd,
+        6, // kAddScalar,
+        6, // kAddString,   // string concat
+        kBracketPrecedence, // kArrayOp,
+        10, // kBitAnd,
+        4, // kBitNot,
+        12, // kBitOr,
+        5, // kDivide,
+        // kDivideInt = kDivide,
+        5, // kDivideScalar,
+        kIfElsePrecedence, // kElse,
+        9, // kEqual,
+        // kEqualInt = kEqual,
+        9, // kEqualScalar,
+        9, // kEqualString,
+        -1, // kFlipOps,
+        8, // kGreaterEqual,
+        // kGreaterEqualInt = kGreaterEqual,
+        8, // kGreaterEqualScalar,
+        8, // kGreaterEqualString,
+        kIfElsePrecedence, // kIf,
+        13, // kLogicalAnd,
+        4, // kLogicalNot,
+        14, // kLogicalOr,
+        4, // kMinus,
+        // kMinusInt = kMinus,
+        4, // kMinusScalar,
+        5, // kModulo,
+        // kModuloInt = kModulo,
+        5, // kModuloScalar,
+        5, // kMultiply,
+        // kMultiplyInt = kMultiply,
+        5, // kMultiplyScalar,
+        kBracketPrecedence, // kParen,
+        7, // kShiftLeft,
+        7, // kShiftRight,  // signed
+        6, // kSubtract,
+        // kSubtractInt = kSubtract,
+        6, // kSubtractScalar,
+        11, // kXor
+};
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static int token_length(const char* start) {
+    char ch = start[0];
+    if (! is_between(ch, 'a' , 'z') &&  ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
+        return -1;
+    int length = 0;
+    do
+        ch = start[++length];
+    while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
+        ch == '_' || ch == '$');
+    return length;
+}
+
+SkScriptEngine::SkScriptEngine(SkOpType returnType) :
+    fTokenLength(0), fReturnType(returnType), fError(kNoError)
+{
+    SkSuppress noInitialSuppress;
+    noInitialSuppress.fOperator = kUnassigned;
+    noInitialSuppress.fOpStackDepth = 0;
+    noInitialSuppress.fSuppress = false;
+    noInitialSuppress.fElse = 0;
+    fSuppressStack.push(noInitialSuppress);
+    *fOpStack.push() = kParen;
+    fTrackArray.appendClear();
+    fTrackString.appendClear();
+}
+
+SkScriptEngine::~SkScriptEngine() {
+    for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
+        delete *stringPtr;
+    for (SkTypedArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
+        delete *arrayPtr;
+}
+
+int SkScriptEngine::arithmeticOp(char ch, char nextChar, bool lastPush) {
+    SkOp op = kUnassigned;
+    bool reverseOperands = false;
+    bool negateResult = false;
+    int advance = 1;
+    switch (ch) {
+        case '+':
+            // !!! ignoring unary plus as implemented here has the side effect of
+            // suppressing errors like +"hi"
+            if (lastPush == false)  // unary plus, don't push an operator
+                goto returnAdv;
+            op = kAdd;
+            break;
+        case '-':
+            op = lastPush ? kSubtract : kMinus;
+            break;
+        case '*':
+            op = kMultiply;
+            break;
+        case '/':
+            op = kDivide;
+            break;
+        case '>':
+            if (nextChar == '>') {
+                op = kShiftRight;
+                goto twoChar;
+            } 
+            op = kGreaterEqual;
+            if (nextChar == '=')
+                goto twoChar;
+            reverseOperands = negateResult = true;
+            break;
+        case '<':
+            if (nextChar == '<') {
+                op = kShiftLeft;
+                goto twoChar;
+            }
+            op = kGreaterEqual;
+            reverseOperands = nextChar == '=';
+            negateResult = ! reverseOperands;
+            advance += reverseOperands;
+            break;
+        case '=':
+            if (nextChar == '=') {
+                op = kEqual;
+                goto twoChar;
+            }
+            break;
+        case '!':
+            if (nextChar == '=') {
+                op = kEqual;
+                negateResult = true;
+twoChar:
+                advance++;
+                break;
+            } 
+            op = kLogicalNot;
+            break;
+        case '?':
+            op = kIf;
+            break;
+        case ':':
+            op = kElse;
+            break;
+        case '^':
+            op = kXor;
+            break;
+        case '(':
+            *fOpStack.push() = kParen;  // push even if eval is suppressed
+            goto returnAdv;
+        case '&':
+            SkASSERT(nextChar != '&');
+            op = kBitAnd;
+            break;
+        case '|':
+            SkASSERT(nextChar != '|');
+            op = kBitOr;
+            break;
+        case '%':
+            op = kModulo;
+            break;
+        case '~':
+            op = kBitNot;
+            break;
+    }
+    if (op == kUnassigned)
+        return 0;
+    if (fSuppressStack.top().fSuppress == false) {
+        signed char precedence = gPrecedence[op];
+        do {
+            int idx = 0;
+            SkOp compare;
+            do {
+                compare = fOpStack.index(idx);
+                if ((compare & kArtificialOp) == 0)
+                    break;
+                idx++;
+            } while (true);
+            signed char topPrecedence = gPrecedence[compare];
+            SkASSERT(topPrecedence != -1);
+            if (topPrecedence > precedence || (topPrecedence == precedence && 
+                    gOpAttributes[op].fLeftType == kNoType)) {
+                break;
+            }
+            if (processOp() == false)
+                return 0;   // error
+        } while (true);
+        if (negateResult)
+            *fOpStack.push() = (SkOp) (kLogicalNot | kArtificialOp);
+        fOpStack.push(op);
+        if (reverseOperands)
+            *fOpStack.push() = (SkOp) (kFlipOps | kArtificialOp);
+    }
+returnAdv:
+    return advance;
+}
+
+void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fBoxCallBack = func;
+    commonCallBack(kBox, callBack, userStorage);
+}
+
+void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) {
+    callBack.fCallBackType = type;
+    callBack.fUserStorage = userStorage;
+    *fUserCallBacks.prepend() = callBack;
+}
+
+bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params, 
+        const SkFunctionParamType* paramTypes, int paramCount) {
+    if (params.count() > paramCount) {
+        fError = kTooManyParameters;
+        return false;   // too many parameters passed
+    }
+    for (int index = 0; index < params.count(); index++) {
+        if (convertTo((SkDisplayTypes) paramTypes[index], &params[index]) == false)
+            return false;
+    }
+    return true;
+}
+
+bool SkScriptEngine::convertTo(SkDisplayTypes toType, SkScriptValue* value ) {
+    SkDisplayTypes type = value->fType;
+    if (type == toType)
+        return true;
+    if (ToOpType(type) == kObject) {
+#if 0   // !!! I want object->string to get string from displaystringtype, not id
+        if (ToOpType(toType) == kString) {
+            bool success = handleObjectToString(value->fOperand.fObject);
+            if (success == false)
+                return false;
+            SkOpType type;
+            fTypeStack.pop(&type);
+            value->fType = ToDisplayType(type);
+            fOperandStack.pop(&value->fOperand);
+            return true;
+        }
+#endif
+        if (handleUnbox(value) == false) {
+            fError = kHandleUnboxFailed;
+            return false;
+        }
+        return convertTo(toType, value);
+    }
+    return ConvertTo(this, toType, value);
+}
+
+bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) { 
+    size_t fieldLength = token_length(++script);        // skip dot
+    if (fieldLength == 0) {
+        fError = kExpectedFieldName;
+        return false;
+    }
+    const char* field = script;
+    script += fieldLength;
+    bool success = handleProperty(suppressed);
+    if (success == false) {
+        fError = kCouldNotFindReferencedID; // note: never generated by standard animator plugins
+        return false;
+    }
+    return evaluateDotParam(script, suppressed, field, fieldLength);
+}
+
+bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed, 
+        const char* field, size_t fieldLength) { 
+    void* object;
+    if (suppressed)
+        object = NULL;
+    else {
+        if (fTypeStack.top() != kObject) {
+            fError = kDotOperatorExpectsObject;
+            return false;
+        }
+        object = fOperandStack.top().fObject;
+        fTypeStack.pop();
+        fOperandStack.pop();
+    }
+    char ch; // see if it is a simple member or a function
+    while (is_ws(ch = script[0])) 
+        script++;
+    bool success = true;
+    if (ch != '(') {
+            if (suppressed == false) {
+                if ((success = handleMember(field, fieldLength, object)) == false)
+                    fError = kHandleMemberFailed;
+            }
+    } else {
+        SkTDArray<SkScriptValue> params;
+        *fBraceStack.push() = kFunctionBrace;
+        success = functionParams(&script, params);
+        if (success && suppressed == false &&
+                (success = handleMemberFunction(field, fieldLength, object, params)) == false) 
+            fError = kHandleMemberFunctionFailed;       
+    }
+    return success; 
+}
+
+bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) {
+#ifdef SK_DEBUG
+    const char** original = scriptPtr;
+#endif
+    bool success;
+    const char* inner;
+    if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
+        *scriptPtr += sizeof("#script:") - 1;
+        if (fReturnType == kNoType || fReturnType == kString) {
+            success = innerScript(scriptPtr, value);
+            if (success == false)
+                goto end;
+            inner = value->fOperand.fString->c_str();
+            scriptPtr = &inner;
+        }
+    }
+    {
+        success = innerScript(scriptPtr, value);
+        if (success == false)
+            goto end;
+        const char* script = *scriptPtr;
+        char ch;
+        while (is_ws(ch = script[0]))
+            script++;
+        if (ch != '\0') {
+            // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
+            fError = kPrematureEnd;
+            success = false;
+        }
+    }
+end:
+#ifdef SK_DEBUG
+    if (success == false) {
+        SkDebugf("script failed: %s", *original);
+        if (fError)
+            SkDebugf(" %s", errorStrings[fError - 1]);
+        SkDebugf("\n");
+    }
+#endif
+    return success;
+}
+
+void SkScriptEngine::forget(SkTypedArray* array) {
+    if (array->getType() == SkType_String) {
+        for (int index = 0; index < array->count(); index++) {
+            SkString* string = (*array)[index].fString;
+            int found = fTrackString.find(string);
+            if (found >= 0)
+                fTrackString.remove(found);
+        }
+        return;
+    }
+    if (array->getType() == SkType_Array) {
+        for (int index = 0; index < array->count(); index++) {
+            SkTypedArray* child = (*array)[index].fArray;
+            forget(child);  // forgets children of child
+            int found = fTrackArray.find(child);
+            if (found >= 0)
+                fTrackArray.remove(found);
+        }
+    }
+}
+
+void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fFunctionCallBack = func;
+    commonCallBack(kFunction, callBack, userStorage);
+}
+
+bool SkScriptEngine::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params) {
+    (*scriptPtr)++; // skip open paren
+    *fOpStack.push() = kParen;
+    *fBraceStack.push() = kFunctionBrace;
+    SkBool suppressed = fSuppressStack.top().fSuppress;
+    do {
+        SkScriptValue value;
+        bool success = innerScript(scriptPtr, suppressed ? NULL : &value);
+        if (success == false) {
+            fError = kErrorInFunctionParameters;
+            return false;
+        }
+        if (suppressed)
+            continue;
+        *params.append() = value;
+    } while ((*scriptPtr)[-1] == ',');
+    fBraceStack.pop();
+    fOpStack.pop(); // pop paren
+    (*scriptPtr)++; // advance beyond close paren
+    return true;
+}
+
+#ifdef SK_DEBUG
+bool SkScriptEngine::getErrorString(SkString* str) const {
+    if (fError)
+        str->set(errorStrings[fError - 1]);
+    return fError != 0;
+}
+#endif
+
+bool SkScriptEngine::innerScript(const char** scriptPtr, SkScriptValue* value) {
+    const char* script = *scriptPtr;
+    char ch;
+    bool lastPush = false;
+    bool success = true;
+    int opBalance = fOpStack.count();
+    int baseBrace = fBraceStack.count();
+    int suppressBalance = fSuppressStack.count();
+    while ((ch = script[0]) != '\0') {
+        if (is_ws(ch)) {
+            script++;
+            continue;
+        }
+        SkBool suppressed = fSuppressStack.top().fSuppress;
+        SkOperand operand;
+        const char* dotCheck;
+        if (fBraceStack.count() > baseBrace) {
+#if 0   // disable support for struct brace
+            if (ch == ':') {
+                SkASSERT(fTokenLength > 0);
+                SkASSERT(fBraceStack.top() == kStructBrace);
+                ++script;
+                SkASSERT(fDisplayable);
+                SkString token(fToken, fTokenLength);
+                fTokenLength = 0;
+                const char* tokenName = token.c_str();
+                const SkMemberInfo* tokenInfo SK_INIT_TO_AVOID_WARNING;
+                if (suppressed == false) {
+                    SkDisplayTypes type = fInfo->getType();
+                    tokenInfo = SkDisplayType::GetMember(type, &tokenName);
+                    SkASSERT(tokenInfo);
+                }
+                SkScriptValue tokenValue;
+                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
+                SkASSERT(success);
+                if (suppressed == false) {
+                    if (tokenValue.fType == SkType_Displayable) {
+                        SkASSERT(SkDisplayType::IsDisplayable(tokenInfo->getType()));
+                        fDisplayable->setReference(tokenInfo, tokenValue.fOperand.fDisplayable);
+                    } else {
+                        if (tokenValue.fType != tokenInfo->getType()) {
+                            if (convertTo(tokenInfo->getType(), &tokenValue) == false)
+                                return false;
+                        }
+                        tokenInfo->writeValue(fDisplayable, NULL, 0, 0, 
+                            (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset),
+                            tokenInfo->getType(), tokenValue);
+                    }
+                }
+                lastPush = false;
+                continue;
+            } else 
+#endif              
+            if (fBraceStack.top() == kArrayBrace) {
+                SkScriptValue tokenValue;
+                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
+                if (success == false) {
+                    fError = kErrorInArrrayIndex;
+                    return false;
+                }
+                if (suppressed == false) {
+#if 0 // no support for structures for now
+                    if (tokenValue.fType == SkType_Structure) {
+                        fArrayOffset += (int) fInfo->getSize(fDisplayable);
+                    } else 
+#endif
+                    {
+                        SkDisplayTypes type = ToDisplayType(fReturnType);
+                        if (fReturnType == kNoType) {
+                            // !!! 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);
+                            else
+                                type = value->fOperand.fArray->getType();
+                        }
+                        if (tokenValue.fType != type) {
+                            if (convertTo(type, &tokenValue) == false)
+                                return false;
+                        }
+                        *value->fOperand.fArray->append() = tokenValue.fOperand;
+                    }
+                }
+                lastPush = false;
+                continue;
+            } else {
+                if (token_length(script) == 0) {
+                    fError = kExpectedToken;
+                    return false;
+                }
+            }
+        }
+        if (lastPush != false && fTokenLength > 0) {
+            if (ch == '(') {
+                *fBraceStack.push() = kFunctionBrace;
+                if (handleFunction(&script, SkToBool(suppressed)) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch == '[') {
+                if (handleProperty(SkToBool(suppressed)) == false)
+                    return false;   // note: never triggered by standard animator plugins
+                if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch != '.') {
+                if (handleProperty(SkToBool(suppressed)) == false)
+                    return false;   // note: never triggered by standard animator plugins
+                lastPush = true;
+                continue;
+            }
+        }
+        if (ch == '0' && (script[1] & ~0x20) == 'X') {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            script += 2;
+            script = SkParse::FindHex(script, (uint32_t*)&operand.fS32);
+            if (script == NULL) {
+                fError = kExpectedHex;
+                return false;
+            }
+            goto intCommon;
+        }
+        if (lastPush == false && ch == '.')
+            goto scalarCommon;
+        if (ch >= '0' && ch <= '9') {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            dotCheck = SkParse::FindS32(script, &operand.fS32);
+            if (dotCheck[0] != '.') {
+                script = dotCheck;
+intCommon:
+                if (suppressed == false)
+                    *fTypeStack.push() = kInt;
+            } else {
+scalarCommon:
+                script = SkParse::FindScalar(script, &operand.fScalar);
+                if (suppressed == false)
+                    *fTypeStack.push() = kScalar;
+            }
+            if (suppressed == false)
+                fOperandStack.push(operand);
+            lastPush = true;
+            continue;
+        }
+        int length = token_length(script);
+        if (length > 0) {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            fToken = script;
+            fTokenLength = length;
+            script += length;
+            lastPush = true;
+            continue;
+        }
+        char startQuote = ch;
+        if (startQuote == '\'' || startQuote == '\"') {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            operand.fString = new SkString();
+            track(operand.fString);
+            ++script;
+
+            // <mrr> this is a lot of calls to append() one char at at time
+            // how hard to preflight script so we know how much to grow fString by?
+            do {
+                if (script[0] == '\\')
+                    ++script;
+                operand.fString->append(script, 1);
+                ++script;
+                if (script[0] == '\0') {
+                    fError = kUnterminatedString;
+                    return false;
+                }
+            } while (script[0] != startQuote);
+            ++script;
+            if (suppressed == false) {
+                *fTypeStack.push() = kString;
+                fOperandStack.push(operand);
+            }
+            lastPush = true;
+            continue;
+        }
+        ;
+        if (ch ==  '.') {
+            if (fTokenLength == 0) {
+                SkScriptValue scriptValue;
+                SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
+                int tokenLength = token_length(++script);
+                const char* token = script;
+                script += tokenLength;
+                if (suppressed == false) {
+                    if (fTypeStack.count() == 0) {
+                        fError = kExpectedTokenBeforeDotOperator;
+                        return false;
+                    }
+                    SkOpType topType;
+                    fTypeStack.pop(&topType);
+                    fOperandStack.pop(&scriptValue.fOperand);
+                    scriptValue.fType = ToDisplayType(topType);
+                    handleBox(&scriptValue);
+                }
+                success = evaluateDotParam(script, SkToBool(suppressed), token, tokenLength);
+                if (success == false)
+                    return false;
+                lastPush = true;
+                continue; 
+            }
+            // get next token, and evaluate immediately
+            success = evaluateDot(script, SkToBool(suppressed));
+            if (success == false)               
+                return false;
+            lastPush = true;
+            continue;
+        }
+        if (ch == '[') {
+            if (lastPush == false) {
+                script++;
+                *fBraceStack.push() = kArrayBrace;
+                if (suppressed)
+                    continue;
+                operand.fArray = value->fOperand.fArray = new SkTypedArray(ToDisplayType(fReturnType));
+                track(value->fOperand.fArray);
+                *fTypeStack.push() = (SkOpType) kArray;
+                fOperandStack.push(operand);
+                continue;
+            }
+            if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
+                return false;
+            lastPush = true;
+            continue;
+        }
+#if 0 // structs not supported for now
+        if (ch == '{') {
+            if (lastPush == false) {
+                script++;
+                *fBraceStack.push() = kStructBrace;
+                if (suppressed)
+                    continue;
+                operand.fS32 = 0;
+                *fTypeStack.push() = (SkOpType) kStruct;
+                fOperandStack.push(operand);
+                continue;
+            }
+            SkASSERT(0); // braces in other contexts aren't supported yet
+        }
+#endif
+        if (ch == ')' && fBraceStack.count() > 0) {
+            SkBraceStyle braceStyle = fBraceStack.top(); 
+            if (braceStyle == kFunctionBrace) {
+                fBraceStack.pop();
+                break;
+            }
+        }
+        if (ch == ',' || ch == ']') {
+            if (ch != ',') {
+                SkBraceStyle match;
+                fBraceStack.pop(&match);
+                if (match != kArrayBrace) {
+                    fError = kMismatchedArrayBrace;
+                    return false;
+                }
+            }
+            script++;
+            // !!! see if brace or bracket is correct closer
+            break;
+        }
+        char nextChar = script[1];
+        int advance = logicalOp(ch, nextChar);
+        if (advance < 0)     // error
+            return false;
+        if (advance == 0) 
+            advance = arithmeticOp(ch, nextChar, lastPush);
+        if (advance == 0) // unknown token
+            return false;
+        if (advance > 0)
+            script += advance;
+        lastPush = ch == ']' || ch == ')';
+    }
+    bool suppressed = SkToBool(fSuppressStack.top().fSuppress);
+    if (fTokenLength > 0) {
+        success = handleProperty(suppressed);
+        if (success == false)
+            return false;   // note: never triggered by standard animator plugins
+    }
+    while (fOpStack.count() > opBalance) {   // leave open paren
+        if ((fError = opError()) != kNoError)
+            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
+        SkString* string = fOperandStack.top().fString;
+        fToken = string->c_str();
+        fTokenLength = string->size();
+        fOperandStack.pop();
+        fTypeStack.pop();
+        success = handleProperty(SkToBool(fSuppressStack.top().fSuppress));
+        if (success == false) { // if it couldn't convert, return string (error?)
+            SkOperand operand;
+            operand.fS32 = 0;
+            *fTypeStack.push() = kString;
+            operand.fString = string;
+            fOperandStack.push(operand);
+        }
+    }
+    if (value) {
+        if (fOperandStack.count() == 0)
+            return false;
+        SkASSERT(fOperandStack.count() >= 1);
+        SkASSERT(fTypeStack.count() >= 1);
+        fOperandStack.pop(&value->fOperand);
+        SkOpType type;
+        fTypeStack.pop(&type);
+        value->fType = ToDisplayType(type);
+//      SkASSERT(value->fType != SkType_Unknown);
+        if (topType != fReturnType && topType == kObject && fReturnType != kNoType) {
+            if (convertTo(ToDisplayType(fReturnType), value) == false)
+                return false;
+        }
+    }
+    while (fSuppressStack.count() > suppressBalance)
+        fSuppressStack.pop();
+    *scriptPtr = script;
+    return true; // no error
+}
+
+void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) {
+    UserCallBack callBack;
+    callBack.fMemberCallBack = member;
+    commonCallBack(kMember, callBack, userStorage);
+}
+
+void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fMemberFunctionCallBack = func;
+    commonCallBack(kMemberFunction, callBack, userStorage);
+}
+
+#if 0
+void SkScriptEngine::objectToStringCallBack(_objectToStringCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fObjectToStringCallBack = func;
+    commonCallBack(kObjectToString, callBack, userStorage);
+}
+#endif
+
+bool SkScriptEngine::handleArrayIndexer(const char** scriptPtr, bool suppressed) {
+    SkScriptValue scriptValue;
+    (*scriptPtr)++;
+    *fOpStack.push() = kParen;
+    *fBraceStack.push() = kArrayBrace;
+    SkOpType saveType = fReturnType;
+    fReturnType = kInt;
+    bool success = innerScript(scriptPtr, suppressed == false ? &scriptValue : NULL);
+    if (success == false)
+        return false;
+    fReturnType = saveType;
+    if (suppressed == false) {
+        if (convertTo(SkType_Int, &scriptValue) == false)
+            return false;
+        int index = scriptValue.fOperand.fS32;
+        SkScriptValue scriptValue;
+        SkOpType type;
+        fTypeStack.pop(&type);
+        fOperandStack.pop(&scriptValue.fOperand);
+        scriptValue.fType = ToDisplayType(type);
+        if (type == kObject) {
+            success = handleUnbox(&scriptValue);
+            if (success == false)
+                return false;
+            if (ToOpType(scriptValue.fType) != kArray) {
+                fError = kExpectedArray;
+                return false;
+            }
+        }
+        *fTypeStack.push() = scriptValue.fOperand.fArray->getOpType();
+//      SkASSERT(index >= 0);
+        if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
+            fError = kArrayIndexOutOfBounds;
+            return false;
+        }
+        scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
+        fOperandStack.push(scriptValue.fOperand);
+    }
+    fOpStack.pop(); // pop paren
+    return success;
+}
+
+bool SkScriptEngine::handleBox(SkScriptValue* scriptValue) {
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kBox)
+            continue;
+        success = (*callBack->fBoxCallBack)(callBack->fUserStorage, scriptValue);
+        if (success) {
+            fOperandStack.push(scriptValue->fOperand);
+            *fTypeStack.push() = ToOpType(scriptValue->fType);
+            goto done;
+        }
+    }
+done:
+    return success;
+}
+
+bool SkScriptEngine::handleFunction(const char** scriptPtr, bool suppressed) {
+    SkScriptValue callbackResult;
+    SkTDArray<SkScriptValue> params;
+    SkString functionName(fToken, fTokenLength);
+    fTokenLength = 0;
+    bool success = functionParams(scriptPtr, params);
+    if (success == false)
+        goto done;
+    if (suppressed == true)
+        return true;
+    {
+        for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+            if (callBack->fCallBackType != kFunction)
+                continue;
+            success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params, 
+                callBack->fUserStorage, &callbackResult);
+            if (success) {
+                fOperandStack.push(callbackResult.fOperand);
+                *fTypeStack.push() = ToOpType(callbackResult.fType);
+                goto done;
+            }
+        }
+    }
+    fError = kNoFunctionHandlerFound;
+    return false;
+done:
+    return success;
+}
+
+bool SkScriptEngine::handleMember(const char* field, size_t len, void* object) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kMember)
+            continue;
+        success = (*callBack->fMemberCallBack)(field, len, object, callBack->fUserStorage, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkType_String)
+                track(callbackResult.fOperand.fString);
+            fOperandStack.push(callbackResult.fOperand);
+            *fTypeStack.push() = ToOpType(callbackResult.fType);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+
+bool SkScriptEngine::handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kMemberFunction)
+            continue;
+        success = (*callBack->fMemberFunctionCallBack)(field, len, object, params, 
+            callBack->fUserStorage, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkType_String)
+                track(callbackResult.fOperand.fString);
+            fOperandStack.push(callbackResult.fOperand);
+            *fTypeStack.push() = ToOpType(callbackResult.fType);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+
+#if 0
+bool SkScriptEngine::handleObjectToString(void* object) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kObjectToString)
+            continue;
+        success = (*callBack->fObjectToStringCallBack)(object, 
+            callBack->fUserStorage, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkType_String)
+                track(callbackResult.fOperand.fString);
+            fOperandStack.push(callbackResult.fOperand);
+            *fTypeStack.push() = ToOpType(callbackResult.fType);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+#endif
+
+bool SkScriptEngine::handleProperty(bool suppressed) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    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, 
+                callBack->fUserStorage, &callbackResult);
+            if (success) {
+                if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) {
+                    callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
+                    track(callbackResult.fOperand.fString);
+                }
+                fOperandStack.push(callbackResult.fOperand);
+                *fTypeStack.push() = ToOpType(callbackResult.fType);
+                goto done;
+            }
+        }
+    }
+done:
+    fTokenLength = 0;
+    return success;
+}
+
+bool SkScriptEngine::handleUnbox(SkScriptValue* scriptValue) {
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kUnbox)
+            continue;
+        success = (*callBack->fUnboxCallBack)(callBack->fUserStorage, scriptValue);
+        if (success) {
+            if (scriptValue->fType == SkType_String)
+                track(scriptValue->fOperand.fString);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+
+// note that entire expression is treated as if it were enclosed in parens
+// an open paren is always the first thing in the op stack
+
+int SkScriptEngine::logicalOp(char ch, char nextChar) {
+    int advance = 1;
+    SkOp match;
+    signed char precedence;
+    switch (ch) {
+        case ')':
+            match = kParen;
+            break;
+        case ']':
+            match = kArrayOp;
+            break;
+        case '?':
+            match = kIf;
+            break;
+        case ':':
+            match = kElse;
+            break;
+        case '&':
+            if (nextChar != '&')
+                goto noMatch;
+            match = kLogicalAnd;
+            advance = 2;
+            break;
+        case '|':
+            if (nextChar != '|')
+                goto noMatch;
+            match = kLogicalOr;
+            advance = 2;
+            break;
+        default:
+noMatch:
+            return 0;
+    }
+    SkSuppress suppress;
+    precedence = gPrecedence[match];
+    if (fSuppressStack.top().fSuppress) {
+        if (fSuppressStack.top().fOpStackDepth < fOpStack.count()) {
+            SkOp topOp = fOpStack.top();
+            if (gPrecedence[topOp] <= precedence)
+                fOpStack.pop();
+            goto goHome;
+        }
+        bool changedPrecedence = gPrecedence[fSuppressStack.top().fOperator] < precedence;
+        if (changedPrecedence)
+            fSuppressStack.pop();
+        if (precedence == kIfElsePrecedence) {
+            if (match == kIf) {
+                if (changedPrecedence)
+                    fOpStack.pop();
+                else
+                    *fOpStack.push() = kIf;
+            } else {
+                if (fSuppressStack.top().fOpStackDepth == fOpStack.count()) {
+                    goto flipSuppress;
+                }
+                fOpStack.pop();
+            }
+        }
+        if (changedPrecedence == false)
+            goto goHome;
+    }
+    while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) {
+        if (processOp() == false)
+            return false;
+    }
+    if (fSuppressStack.top().fOpStackDepth > fOpStack.count())
+        fSuppressStack.pop();
+    switch (match) {
+        case kParen:
+        case kArrayOp:
+            if (fOpStack.count() <= 1 || fOpStack.top() != match) {
+                fError = kMismatchedBrackets;
+                return -1;
+            }
+            if (match == kParen) 
+                fOpStack.pop();
+            else {
+                SkOpType indexType;
+                fTypeStack.pop(&indexType);
+                if (indexType != kInt && indexType != kScalar) {
+                    fError = kExpectedNumberForArrayIndex; // (although, could permit strings eventually)
+                    return -1;
+                }
+                SkOperand indexOperand;
+                fOperandStack.pop(&indexOperand);
+                int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) : 
+                    indexOperand.fS32;
+                SkOpType arrayType;
+                fTypeStack.pop(&arrayType);
+                if ((unsigned)arrayType != (unsigned)kArray) {
+                    fError = kExpectedArray;
+                    return -1;
+                }
+                SkOperand arrayOperand;
+                fOperandStack.pop(&arrayOperand);
+                SkTypedArray* array = arrayOperand.fArray;
+                SkOperand operand;
+                if (array->getIndex(index, &operand) == false) {
+                    fError = kIndexOutOfRange;
+                    return -1;
+                }
+                SkOpType resultType = array->getOpType();
+                fTypeStack.push(resultType);
+                fOperandStack.push(operand);
+            }
+            break;
+        case kIf: {
+            SkScriptValue ifValue;
+            SkOpType ifType;
+            fTypeStack.pop(&ifType);
+            ifValue.fType = ToDisplayType(ifType);
+            fOperandStack.pop(&ifValue.fOperand);
+            if (convertTo(SkType_Int, &ifValue) == false)
+                return -1;
+            if (ifValue.fType != SkType_Int) {
+                fError = kExpectedIntForConditionOperator;
+                return -1;
+            }
+            suppress.fSuppress = ifValue.fOperand.fS32 == 0;
+            suppress.fOperator = kIf;
+            suppress.fOpStackDepth = fOpStack.count(); 
+            suppress.fElse = false;
+            fSuppressStack.push(suppress);
+            // if left is true, do only up to colon
+            // if left is false, do only after colon
+            } break;
+        case kElse:
+flipSuppress:
+            if (fSuppressStack.top().fElse)
+                fSuppressStack.pop();
+            fSuppressStack.top().fElse = true;
+            fSuppressStack.top().fSuppress ^= true;
+            // flip last do / don't do consideration from last '?'
+            break;
+        case kLogicalAnd:
+        case kLogicalOr: {
+            if (fTypeStack.top() != kInt) {
+                fError = kExpectedBooleanExpression;
+                return -1;
+            }
+            int32_t topInt = fOperandStack.top().fS32;
+            if (fOpStack.top() != kLogicalAnd)
+                *fOpStack.push() = kLogicalAnd; // really means 'to bool', and is appropriate for 'or'
+            if (match == kLogicalOr ? topInt != 0 : topInt == 0) {
+                suppress.fSuppress = true;
+                suppress.fOperator = match;
+                suppress.fOpStackDepth = fOpStack.count(); 
+                suppress.fElse = false;
+                fSuppressStack.push(suppress);
+            } else {
+                fTypeStack.pop();
+                fOperandStack.pop();
+            }
+        }   break;
+        default:
+            SkASSERT(0);
+    }
+goHome:
+    return advance;
+}
+
+SkScriptEngine::Error SkScriptEngine::opError() {
+    int opCount = fOpStack.count();
+    int operandCount = fOperandStack.count();
+    if (opCount == 0) {
+        if (operandCount != 1)
+            return kExpectedOperator;
+        return kNoError;
+    }
+    SkOp op = (SkOp) (fOpStack.top() & ~kArtificialOp);
+    const SkOperatorAttributes* attributes = &gOpAttributes[op];
+    if (attributes->fLeftType != kNoType && operandCount < 2)
+        return kExpectedValue;
+    if (attributes->fLeftType == kNoType && operandCount < 1)
+        return kExpectedValue;
+    return kNoError;
+}
+
+bool SkScriptEngine::processOp() {
+    SkOp op;
+    fOpStack.pop(&op);
+    op = (SkOp) (op & ~kArtificialOp);
+    const SkOperatorAttributes* attributes = &gOpAttributes[op];
+    SkOpType type2;
+    fTypeStack.pop(&type2);
+    SkOpType type1 = type2;
+    SkOperand operand2;
+    fOperandStack.pop(&operand2);
+    SkOperand operand1 = operand2; // !!! not really needed, suppresses warning
+    if (attributes->fLeftType != kNoType) {
+        fTypeStack.pop(&type1);
+        fOperandStack.pop(&operand1);
+        if (op == kFlipOps) {
+            SkTSwap(type1, type2);
+            SkTSwap(operand1, operand2);
+            fOpStack.pop(&op);
+            op = (SkOp) (op & ~kArtificialOp);
+            attributes = &gOpAttributes[op];
+        }
+        if (type1 == kObject && (type1 & attributes->fLeftType) == 0) {
+            SkScriptValue val;
+            val.fType = ToDisplayType(type1);
+            val.fOperand = operand1;
+            bool success = handleUnbox(&val);
+            if (success == false)
+                return false;
+            type1 = ToOpType(val.fType);
+            operand1 = val.fOperand;
+        }
+    }
+    if (type2 == kObject && (type2 & attributes->fLeftType) == 0) {
+        SkScriptValue val;
+        val.fType = ToDisplayType(type2);
+        val.fOperand = operand2;
+        bool success = handleUnbox(&val);
+        if (success == false)
+            return false;
+        type2 = ToOpType(val.fType);
+        operand2 = val.fOperand;
+    }
+    if (attributes->fLeftType != kNoType) {
+        if (type1 != type2) {
+            if ((attributes->fLeftType & kString) && attributes->fBias & kTowardsString && ((type1 | type2) & kString)) {
+                if (type1 == kInt || type1 == kScalar) {
+                    convertToString(operand1, type1 == kInt ? SkType_Int : SkType_Float);
+                    type1 = kString;
+                }
+                if (type2 == kInt || type2 == kScalar) {
+                    convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float);
+                    type2 = kString;
+                }
+            } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) {
+                if (type1 == kInt) {
+                    operand1.fScalar = IntToScalar(operand1.fS32);
+                    type1 = kScalar;
+                }
+                if (type2 == kInt) {
+                    operand2.fScalar = IntToScalar(operand2.fS32);
+                     type2 = kScalar;
+                }
+            }
+        }
+        if ((type1 & attributes->fLeftType) == 0 || type1 != type2) {
+            if (type1 == kString) {
+                const char* result = SkParse::FindScalar(operand1.fString->c_str(), &operand1.fScalar);
+                if (result == NULL) {
+                    fError = kExpectedNumber;
+                    return false;
+                }
+                type1 = kScalar;
+            }
+            if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) {
+                operand1.fS32 = SkScalarFloor(operand1.fScalar);
+                type1 = kInt;
+            }
+        }
+    }
+    if ((type2 & attributes->fRightType) == 0 || type1 != type2) {
+        if (type2 == kString) {
+            const char* result = SkParse::FindScalar(operand2.fString->c_str(), &operand2.fScalar);
+            if (result == NULL) {
+                fError = kExpectedNumber;
+                return false;
+            }
+            type2 = kScalar;
+        }
+        if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) {
+            operand2.fS32 = SkScalarFloor(operand2.fScalar);
+            type2 = kInt;
+        }
+    }
+    if (type2 == kScalar)
+        op = (SkOp) (op + 1);
+    else if (type2 == kString)
+        op = (SkOp) (op + 2);
+    switch(op) {
+        case kAddInt:
+            operand2.fS32 += operand1.fS32;
+            break;
+        case kAddScalar:
+            operand2.fScalar += operand1.fScalar;
+            break;
+        case kAddString:
+            if (fTrackString.find(operand1.fString) < 0) {
+                operand1.fString = SkNEW_ARGS(SkString, (*operand1.fString));
+                track(operand1.fString);
+            }
+            operand1.fString->append(*operand2.fString);
+            operand2 = operand1;
+            break;
+        case kBitAnd:
+            operand2.fS32 &= operand1.fS32;
+            break;
+        case kBitNot:
+            operand2.fS32 = ~operand2.fS32;
+            break;
+        case kBitOr:
+            operand2.fS32 |= operand1.fS32;
+            break;
+        case kDivideInt:
+            if (operand2.fS32 == 0) {
+                operand2.fS32 = operand1.fS32 == 0 ? SK_NaN32 : operand1.fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
+                break;
+            } else {
+                int32_t original = operand2.fS32;
+                operand2.fS32 = operand1.fS32 / operand2.fS32;
+                if (original * operand2.fS32 == operand1.fS32)
+                    break;    // integer divide was good enough
+                operand2.fS32 = original;
+                type2 = kScalar;
+            }
+        case kDivideScalar:
+            if (operand2.fScalar == 0)
+                operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
+            else
+                operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar);
+            break;
+        case kEqualInt:
+            operand2.fS32 = operand1.fS32 == operand2.fS32;
+            break;
+        case kEqualScalar:
+            operand2.fS32 = operand1.fScalar == operand2.fScalar;
+            type2 = kInt;
+            break;
+        case kEqualString:
+            operand2.fS32 = *operand1.fString == *operand2.fString;
+            type2 = kInt;
+            break;
+        case kGreaterEqualInt:
+            operand2.fS32 = operand1.fS32 >= operand2.fS32;
+            break;
+        case kGreaterEqualScalar:
+            operand2.fS32 = operand1.fScalar >= operand2.fScalar;
+            type2 = kInt;
+            break;
+        case kGreaterEqualString:
+            operand2.fS32 = strcmp(operand1.fString->c_str(), operand2.fString->c_str()) >= 0;
+            type2 = kInt;
+            break;
+        case kLogicalAnd:
+            operand2.fS32 = !! operand2.fS32;   // really, ToBool
+            break;
+        case kLogicalNot:
+            operand2.fS32 = ! operand2.fS32;
+            break;
+        case kLogicalOr:
+            SkASSERT(0);    // should have already been processed
+            break;
+        case kMinusInt:
+            operand2.fS32 = -operand2.fS32;
+            break;
+        case kMinusScalar:
+            operand2.fScalar = -operand2.fScalar;
+            break;
+        case kModuloInt:
+            operand2.fS32 = operand1.fS32 % operand2.fS32;
+            break;
+        case kModuloScalar:
+            operand2.fScalar = SkScalarMod(operand1.fScalar, operand2.fScalar);
+            break;
+        case kMultiplyInt:
+            operand2.fS32 *= operand1.fS32;
+            break;
+        case kMultiplyScalar:
+            operand2.fScalar = SkScalarMul(operand1.fScalar, operand2.fScalar);
+            break;
+        case kShiftLeft:
+            operand2.fS32 = operand1.fS32 << operand2.fS32;
+            break;
+        case kShiftRight:
+            operand2.fS32 = operand1.fS32 >> operand2.fS32;
+            break;
+        case kSubtractInt:
+            operand2.fS32 = operand1.fS32 - operand2.fS32;
+            break;
+        case kSubtractScalar:
+            operand2.fScalar = operand1.fScalar - operand2.fScalar;
+            break;
+        case kXor:
+            operand2.fS32 ^= operand1.fS32;
+            break;
+        default:
+            SkASSERT(0);
+    }
+    fTypeStack.push(type2);
+    fOperandStack.push(operand2);
+    return true;
+}
+
+void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fPropertyCallBack = prop;
+    commonCallBack(kProperty, callBack, userStorage);
+}
+
+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::unboxCallBack(_unboxCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fUnboxCallBack = func;
+    commonCallBack(kUnbox, callBack, userStorage);
+}
+
+bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, SkScriptValue* value ) {
+    SkASSERT(value);
+    if (SkDisplayType::IsEnum(NULL /* fMaker */, toType))
+        toType = SkType_Int;
+    if (toType == SkType_Point || toType == SkType_3D_Point)
+        toType = SkType_Float;
+    if (toType == SkType_Drawable)
+        toType = SkType_Displayable;
+    SkDisplayTypes type = value->fType;
+    if (type == toType) 
+        return true;
+    SkOperand& operand = value->fOperand;
+    bool success = true;
+    switch (toType) {
+        case SkType_Int:
+            if (type == SkType_Boolean)
+                break;
+            if (type == SkType_Float)
+                operand.fS32 = SkScalarFloor(operand.fScalar);
+            else {
+                if (type != SkType_String) {
+                    success = false;
+                    break; // error
+                }
+                success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
+            }
+            break;
+        case SkType_Float:
+            if (type == SkType_Int) {
+                if ((uint32_t)operand.fS32 == SK_NaN32)
+                    operand.fScalar = SK_ScalarNaN;
+                else if (SkAbs32(operand.fS32) == SK_MaxS32)
+                    operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax;
+                else
+                    operand.fScalar = SkIntToScalar(operand.fS32);
+            } else {
+                if (type != SkType_String) {
+                    success = false;
+                    break; // error
+                }
+                success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
+            }
+            break;
+        case SkType_String: {
+            SkString* strPtr = new SkString();
+            SkASSERT(engine);
+            engine->track(strPtr);
+            if (type == SkType_Int)
+                strPtr->appendS32(operand.fS32);
+            else if (type == SkType_Displayable) 
+                SkASSERT(0); // must call through instance version instead of static version
+            else {
+                if (type != SkType_Float) {
+                    success = false;
+                    break;
+                }
+                strPtr->appendScalar(operand.fScalar);
+            }
+            operand.fString = strPtr;
+            } break;
+        case SkType_Array: {
+            SkTypedArray* array = new SkTypedArray(type);
+            *array->append() = operand;
+            engine->track(array);
+            operand.fArray = array;
+            } break;
+        default:
+            SkASSERT(0);
+    }
+    value->fType = toType;
+    if (success == false)
+        engine->fError = kTypeConversionFailed;
+    return success;
+}
+
+SkScalar SkScriptEngine::IntToScalar(int32_t s32) {
+    SkScalar scalar;
+    if ((uint32_t)s32 == SK_NaN32)
+        scalar = SK_ScalarNaN;
+    else if (SkAbs32(s32) == SK_MaxS32)
+        scalar = SkSign32(s32) * SK_ScalarMax;
+    else
+        scalar = SkIntToScalar(s32);
+    return scalar;
+}
+
+SkDisplayTypes SkScriptEngine::ToDisplayType(SkOpType type) {
+    int val = type;
+    switch (val) {
+        case kNoType:
+            return SkType_Unknown;
+        case kInt:
+            return SkType_Int;
+        case kScalar:
+            return SkType_Float;
+        case kString:
+            return SkType_String;
+        case kArray:
+            return SkType_Array;
+        case kObject:
+            return SkType_Displayable;
+//      case kStruct:
+//          return SkType_Structure;
+        default:
+            SkASSERT(0);
+            return SkType_Unknown;
+    }
+}
+
+SkScriptEngine::SkOpType SkScriptEngine::ToOpType(SkDisplayTypes type) {
+    if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
+        return (SkOpType) kObject;
+    if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
+        return kInt;
+    switch (type) {
+        case SkType_ARGB:
+        case SkType_MSec:
+        case SkType_Int:
+            return kInt;
+        case SkType_Float:
+        case SkType_Point:
+        case SkType_3D_Point:
+            return kScalar;
+        case SkType_Base64:
+        case SkType_DynamicString:
+        case SkType_String:
+            return kString;
+        case SkType_Array:
+            return (SkOpType) kArray;
+        case SkType_Unknown:
+            return kNoType;
+        default:
+            SkASSERT(0);
+            return kNoType;
+    }
+}
+
+bool SkScriptEngine::ValueToString(SkScriptValue value, SkString* string) {
+    switch (value.fType) {
+        case kInt:
+            string->reset();
+            string->appendS32(value.fOperand.fS32);
+            break;
+        case kScalar:
+            string->reset();
+            string->appendScalar(value.fOperand.fScalar);
+            break;
+        case kString:
+            string->set(*value.fOperand.fString);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true; // no error
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+
+#ifdef SK_CAN_USE_FLOAT
+    #include "SkFloatingPoint.h"
+#endif
+
+#define DEF_SCALAR_ANSWER   0
+#define DEF_STRING_ANSWER   NULL
+
+#define testInt(expression) { #expression, SkType_Int, expression, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
+#ifdef SK_SCALAR_IS_FLOAT
+    #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
+#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 }
+
+static const SkScriptNAnswer scriptTests[]  = {
+    testInt(1>1/2),
+    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), 
+    testRemainder(9,2.5),
+    testRemainder(-9,2.5),
+    testTrue(-9==-9.0),
+    testTrue(-9.==-4.0-5),
+    testTrue(-9.*1==-4-5),
+    testFalse(-9!=-9.0),
+    testFalse(-9.!=-4.0-5),
+    testFalse(-9.*1!=-4-5),
+#endif
+    testInt(0x123),
+    testInt(0XABC),
+    testInt(0xdeadBEEF),
+    {   "'123'+\"456\"", SkType_String, 0, 0, "123456" },
+    {   "123+\"456\"", SkType_String, 0, 0, "123456" },
+    {   "'123'+456", SkType_String, 0, 0, "123456" },
+    {   "'123'|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "123|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "'123'|456", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "'2'<11", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "2<'11'", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "'2'<'11'", SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    testInt(123),
+    testInt(-345),
+    testInt(+678),
+    testInt(1+2+3),
+    testInt(3*4+5),
+    testInt(6+7*8),
+    testInt(-1-2-8/4),
+    testInt(-9%4),
+    testInt(9%-4),
+    testInt(-9%-4),
+    testInt(123|978),
+    testInt(123&978),
+    testInt(123^978),
+    testInt(2<<4),
+    testInt(99>>3),
+    testInt(~55),
+    testInt(~~55),
+    testInt(!55),
+    testInt(!!55),
+    // both int
+    testInt(2<2),
+    testInt(2<11),
+    testInt(20<11),
+    testInt(2<=2),
+    testInt(2<=11),
+    testInt(20<=11),
+    testInt(2>2),
+    testInt(2>11),
+    testInt(20>11),
+    testInt(2>=2),
+    testInt(2>=11),
+    testInt(20>=11),
+    testInt(2==2),
+    testInt(2==11),
+    testInt(20==11),
+    testInt(2!=2),
+    testInt(2!=11),
+    testInt(20!=11),
+#ifdef SK_CAN_USE_FLOAT
+    // left int, right scalar
+    testInt(2<2.),
+    testInt(2<11.),
+    testInt(20<11.),
+    testInt(2<=2.),
+    testInt(2<=11.),
+    testInt(20<=11.),
+    testInt(2>2.),
+    testInt(2>11.),
+    testInt(20>11.),
+    testInt(2>=2.),
+    testInt(2>=11.),
+    testInt(20>=11.),
+    testInt(2==2.),
+    testInt(2==11.),
+    testInt(20==11.),
+    testInt(2!=2.),
+    testInt(2!=11.),
+    testInt(20!=11.),
+    // left scalar, right int
+        testInt(2.<2),
+    testInt(2.<11),
+    testInt(20.<11),
+    testInt(2.<=2),
+    testInt(2.<=11),
+    testInt(20.<=11),
+    testInt(2.>2),
+    testInt(2.>11),
+    testInt(20.>11),
+    testInt(2.>=2),
+    testInt(2.>=11),
+    testInt(20.>=11),
+    testInt(2.==2),
+    testInt(2.==11),
+    testInt(20.==11),
+    testInt(2.!=2),
+    testInt(2.!=11),
+    testInt(20.!=11),
+    // both scalar
+    testInt(2.<11.),
+    testInt(20.<11.),
+    testInt(2.<=2.),
+    testInt(2.<=11.),
+    testInt(20.<=11.),
+    testInt(2.>2.),
+    testInt(2.>11.),
+    testInt(20.>11.),
+    testInt(2.>=2.),
+    testInt(2.>=11.),
+    testInt(20.>=11.),
+    testInt(2.==2.),
+    testInt(2.==11.),
+    testInt(20.==11.),
+    testInt(2.!=2.),
+    testInt(2.!=11.),
+    testInt(20.!=11.),
+#endif
+    // int, string (string is int)
+    testFalse(2<'2'),
+    testTrue(2<'11'),
+    testFalse(20<'11'),
+    testTrue(2<='2'),
+    testTrue(2<='11'),
+    testFalse(20<='11'),
+    testFalse(2>'2'),
+    testFalse(2>'11'),
+    testTrue(20>'11'),
+    testTrue(2>='2'),
+    testFalse(2>='11'),
+    testTrue(20>='11'),
+    testTrue(2=='2'),
+    testFalse(2=='11'),
+    testFalse(2!='2'),
+    testTrue(2!='11'),
+    // int, string (string is scalar)
+    testFalse(2<'2.'),
+    testTrue(2<'11.'),
+    testFalse(20<'11.'),
+    testTrue(2=='2.'),
+    testFalse(2=='11.'),
+#ifdef SK_CAN_USE_FLOAT
+    // scalar, string
+    testFalse(2.<'2.'),
+    testTrue(2.<'11.'),
+    testFalse(20.<'11.'),
+    testTrue(2.=='2.'),
+    testFalse(2.=='11.'),
+    // string, int
+    testFalse('2'<2),
+    testTrue('2'<11),
+    testFalse('20'<11),
+    testTrue('2'==2),
+    testFalse('2'==11),
+    // string, scalar
+    testFalse('2'<2.),
+    testTrue('2'<11.),
+    testFalse('20'<11.),
+    testTrue('2'==2.),
+    testFalse('2'==11.),
+#endif
+    // string, string
+    testFalse('2'<'2'),
+    testFalse('2'<'11'),
+    testFalse('20'<'11'),
+    testTrue('2'=='2'),
+    testFalse('2'=='11'),
+    // logic
+    testInt(1?2:3),
+    testInt(0?2:3),
+    testInt(1&&2||3),
+    testInt(1&&0||3),
+    testInt(1&&0||0),
+    testInt(1||0&&3),
+    testInt(0||0&&3),
+    testInt(0||1&&3),
+    testInt(1?(2?3:4):5),
+    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),
+    testInt(0?0?3:4:5),
+    
+    testInt(1?2:(3?4:5)),
+    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),
+    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)
+
+void SkScriptEngine::UnitTest() {
+    for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) {
+        SkScriptEngine engine(SkScriptEngine::ToOpType(scriptTests[index].fType));
+        SkScriptValue value;
+        const char* script = scriptTests[index].fScript;
+        SkASSERT(engine.evaluateScript(&script, &value) == true);
+        SkASSERT(value.fType == scriptTests[index].fType);
+        SkScalar error;
+        switch (value.fType) {
+            case SkType_Int:
+                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+                break;
+            case SkType_Float:
+                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
+                SkASSERT(error < SK_Scalar1 / 10000);
+                break;
+            case SkType_String:
+                SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0);
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+}
+#endif
+
diff --git a/legacy/src/animator/SkScript.h b/legacy/src/animator/SkScript.h
new file mode 100644
index 0000000..95930e8
--- /dev/null
+++ b/legacy/src/animator/SkScript.h
@@ -0,0 +1,266 @@
+
+/*
+ * 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 SkScript_DEFINED
+#define SkScript_DEFINED
+
+#include "SkOperand.h"
+#include "SkIntArray.h"
+#include "SkTDict.h"
+#include "SkTDStack.h"
+
+class SkAnimateMaker;
+
+class SkScriptEngine {
+public:
+    enum Error {
+        kNoError,
+        kArrayIndexOutOfBounds,
+        kCouldNotFindReferencedID,
+        kDotOperatorExpectsObject,
+        kErrorInArrrayIndex,
+        kErrorInFunctionParameters,
+        kExpectedArray,
+        kExpectedBooleanExpression,
+        kExpectedFieldName,
+        kExpectedHex,
+        kExpectedIntForConditionOperator,
+        kExpectedNumber,
+        kExpectedNumberForArrayIndex,
+        kExpectedOperator,
+        kExpectedToken,
+        kExpectedTokenBeforeDotOperator,
+        kExpectedValue,
+        kHandleMemberFailed,
+        kHandleMemberFunctionFailed,
+        kHandleUnboxFailed,
+        kIndexOutOfRange,
+        kMismatchedArrayBrace,
+        kMismatchedBrackets,
+        kNoFunctionHandlerFound,
+        kPrematureEnd,
+        kTooManyParameters,
+        kTypeConversionFailed,
+        kUnterminatedString
+    };
+
+    enum SkOpType {
+        kNoType,
+        kInt = 1,
+        kScalar = 2,
+        kString = 4,
+        kArray = 8,
+        kObject = 16
+//      kStruct = 32
+    };
+
+    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, 
+        void* userStorage, SkScriptValue* result);
+    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);
+    typedef bool (*_unboxCallBack)(void* userStorage, SkScriptValue* result);
+    SkScriptEngine(SkOpType returnType);
+    ~SkScriptEngine();
+    void boxCallBack(_boxCallBack func, void* userStorage);
+    bool convertTo(SkDisplayTypes , SkScriptValue* );
+    bool evaluateScript(const char** script, SkScriptValue* value);
+    void forget(SkTypedArray* array);
+    void functionCallBack(_functionCallBack func, void* userStorage);
+    Error getError() const { return fError; }
+#ifdef SK_DEBUG
+    bool getErrorString(SkString* err) const;
+#endif
+    void memberCallBack(_memberCallBack , void* userStorage);
+    void memberFunctionCallBack(_memberFunctionCallBack , void* userStorage);
+//  void objectToStringCallBack(_objectToStringCallBack , void* userStorage);
+    void propertyCallBack(_propertyCallBack prop, void* userStorage);
+    void track(SkTypedArray* array);
+    void track(SkString* string);
+    void unboxCallBack(_unboxCallBack func, void* userStorage);
+    static bool ConvertTo(SkScriptEngine* , SkDisplayTypes toType, SkScriptValue* value);
+    static SkScalar IntToScalar(int32_t );
+    static SkDisplayTypes ToDisplayType(SkOpType type);
+    static SkOpType ToOpType(SkDisplayTypes type);
+    static bool ValueToString(SkScriptValue value, SkString* string);
+
+    enum CallBackType {
+        kBox,
+        kFunction,
+        kMember,
+        kMemberFunction,
+    //  kObjectToString,
+        kProperty,
+        kUnbox
+    };
+
+    struct UserCallBack {
+        CallBackType fCallBackType;
+        void* fUserStorage;
+        union {
+            _boxCallBack fBoxCallBack;
+            _functionCallBack fFunctionCallBack;
+            _memberCallBack fMemberCallBack;
+            _memberFunctionCallBack fMemberFunctionCallBack;
+    //      _objectToStringCallBack fObjectToStringCallBack;
+            _propertyCallBack fPropertyCallBack;
+            _unboxCallBack fUnboxCallBack;
+        };
+    };
+
+    enum SkOp {
+        kUnassigned,
+        kAdd,
+        kAddInt = kAdd,
+        kAddScalar,
+        kAddString, // string concat
+        kArrayOp,
+        kBitAnd,
+        kBitNot,
+        kBitOr,
+        kDivide,
+        kDivideInt = kDivide,
+        kDivideScalar,
+        kElse,
+        kEqual,
+        kEqualInt = kEqual,
+        kEqualScalar,
+        kEqualString,
+        kFlipOps,
+        kGreaterEqual,
+        kGreaterEqualInt = kGreaterEqual,
+        kGreaterEqualScalar,
+        kGreaterEqualString,
+        kIf,
+        kLogicalAnd,
+        kLogicalNot,
+        kLogicalOr,
+        kMinus,
+        kMinusInt = kMinus,
+        kMinusScalar,
+        kModulo,
+        kModuloInt = kModulo,
+        kModuloScalar,
+        kMultiply,
+        kMultiplyInt = kMultiply,
+        kMultiplyScalar,
+        kParen,
+        kShiftLeft,
+        kShiftRight,    // signed
+        kSubtract,
+        kSubtractInt = kSubtract,
+        kSubtractScalar,
+        kXor,
+        kArtificialOp = 0x40
+    };
+
+    enum SkOpBias {
+        kNoBias,
+        kTowardsNumber = 0,
+        kTowardsString
+    };
+    
+protected:
+
+    struct SkOperatorAttributes {
+        unsigned int fLeftType : 3; // SkOpType, but only lower values
+        unsigned int fRightType : 3;     // SkOpType, but only lower values
+        SkOpBias fBias : 1;
+    };
+
+    struct SkSuppress { // !!! could be compressed to a long
+        SkOp fOperator; // operand which enabled suppression
+        int fOpStackDepth; // depth when suppression operator was found
+        SkBool8 fSuppress; // set if suppression happens now, as opposed to later
+        SkBool8 fElse; // set on the : half of ? :
+    };
+
+    static const SkOperatorAttributes gOpAttributes[];
+    static const signed char gPrecedence[];
+    int arithmeticOp(char ch, char nextChar, bool lastPush);
+    void commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage);
+    bool convertParams(SkTDArray<SkScriptValue>&, const SkFunctionParamType* ,
+                                    int paramTypeCount);
+    void convertToString(SkOperand& operand, SkDisplayTypes type) {
+        SkScriptValue scriptValue;
+        scriptValue.fOperand = operand;
+        scriptValue.fType = type;
+        convertTo(SkType_String, &scriptValue);
+        operand = scriptValue.fOperand;
+    }
+    bool evaluateDot(const char*& script, bool suppressed);
+    bool evaluateDotParam(const char*& script, bool suppressed, const char* field, size_t fieldLength);
+    bool functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params);
+    bool handleArrayIndexer(const char** scriptPtr, bool suppressed);
+    bool handleBox(SkScriptValue* value);
+    bool handleFunction(const char** scriptPtr, bool suppressed);
+    bool handleMember(const char* field, size_t len, void* object);
+    bool handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params);
+//  bool handleObjectToString(void* object);
+    bool handleProperty(bool suppressed);
+    bool handleUnbox(SkScriptValue* scriptValue);
+    bool innerScript(const char** scriptPtr, SkScriptValue* value);
+    int logicalOp(char ch, char nextChar);
+    Error opError();
+    bool processOp();
+    void setAnimateMaker(SkAnimateMaker* maker) { fMaker = maker; }
+    bool setError(Error , const char* pos);
+    enum SkBraceStyle {
+    //  kStructBrace,
+        kArrayBrace,
+        kFunctionBrace
+    };
+
+#if 0
+    SkIntArray(SkBraceStyle) fBraceStack;       // curly, square, function paren
+    SkIntArray(SkOp) fOpStack;
+    SkIntArray(SkOpType) fTypeStack;
+    SkTDOperandArray fOperandStack;
+    SkTDArray<SkSuppress> fSuppressStack;
+#else
+    SkTDStack<SkBraceStyle> fBraceStack;        // curly, square, function paren
+    SkTDStack<SkOp> fOpStack;
+    SkTDStack<SkOpType> fTypeStack;
+    SkTDStack<SkOperand> fOperandStack;
+    SkTDStack<SkSuppress> fSuppressStack;
+#endif
+    SkAnimateMaker* fMaker;
+    SkTDTypedArrayArray fTrackArray;
+    SkTDStringArray fTrackString;
+    const char* fToken; // one-deep stack
+    size_t fTokenLength;
+    SkTDArray<UserCallBack> fUserCallBacks;
+    SkOpType fReturnType;
+    Error fError;
+    int fErrorPosition;
+private:
+    friend class SkTypedArray;
+#ifdef SK_SUPPORT_UNITTEST
+public:
+    static void UnitTest();
+#endif
+};
+
+#ifdef SK_SUPPORT_UNITTEST
+
+struct SkScriptNAnswer {
+    const char* fScript;
+    SkDisplayTypes fType;
+    int32_t fIntAnswer;
+    SkScalar fScalarAnswer;
+    const char* fStringAnswer;
+};
+
+#endif
+
+#endif // SkScript_DEFINED
diff --git a/legacy/src/animator/SkScript2.h b/legacy/src/animator/SkScript2.h
new file mode 100644
index 0000000..05073e4
--- /dev/null
+++ b/legacy/src/animator/SkScript2.h
@@ -0,0 +1,292 @@
+
+/*
+ * 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 SkScript2_DEFINED
+#define SkScript2_DEFINED
+
+#include "SkOperand2.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTDArray_Experimental.h"
+#include "SkTDict.h"
+#include "SkTDStack.h"
+
+typedef SkLongArray(SkString*) SkTDStringArray; 
+
+class SkAnimateMaker;
+class SkScriptCallBack;
+
+class SkScriptEngine2 {
+public:
+	enum Error {
+		kNoError,
+		kArrayIndexOutOfBounds,
+		kCouldNotFindReferencedID,
+		kFunctionCallFailed,
+		kMemberOpFailed,
+		kPropertyOpFailed
+	};
+
+	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);
+
+	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
+	};
+
+	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
+	};
+
+protected:
+
+	enum BraceStyle {
+	//	kStructBrace,
+		kArrayBrace,
+		kFunctionBrace
+	};
+
+	enum AddTokenRegister {
+		kAccumulator,
+		kOperand
+	};
+	
+	enum ResultIsBoolean {
+		kResultIsNotBoolean,
+		kResultIsBoolean
+	};
+
+	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 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();
+#endif
+};
+
+#ifdef SK_DEBUG
+
+struct SkScriptNAnswer2 {
+	const char* fScript;
+	SkOperand2::OpType fType;
+	int32_t fIntAnswer;
+	SkScalar fScalarAnswer;
+	const char* fStringAnswer;
+};
+
+#endif
+
+
+#endif // SkScript2_DEFINED
+
diff --git a/legacy/src/animator/SkScriptCallBack.h b/legacy/src/animator/SkScriptCallBack.h
new file mode 100644
index 0000000..b2a1958
--- /dev/null
+++ b/legacy/src/animator/SkScriptCallBack.h
@@ -0,0 +1,65 @@
+
+/*
+ * 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 SkScriptCallBack_DEFINED
+#define SkScriptCallBack_DEFINED
+
+#include "SkOperand2.h"
+#include "SkTDArray_Experimental.h"
+
+class SkScriptCallBack {
+public:
+	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;
+};
+
+class SkScriptCallBackConvert : public SkScriptCallBack {
+public:
+	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;
+};
+
+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;
+};
+
+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;
+};
+
+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; }
+};
+
+#endif // SkScriptCallBack_DEFINED
diff --git a/legacy/src/animator/SkScriptDecompile.cpp b/legacy/src/animator/SkScriptDecompile.cpp
new file mode 100644
index 0000000..933e0d7
--- /dev/null
+++ b/legacy/src/animator/SkScriptDecompile.cpp
@@ -0,0 +1,215 @@
+
+/*
+ * 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 "SkScript2.h"
+
+#ifdef SK_DEBUG
+
+#define TypeOpName(op) {SkScriptEngine2::op, #op }
+
+static const struct OpName {
+    SkScriptEngine2::TypeOp fOp;
+    const char* fName;
+} gOpNames[] = {
+    TypeOpName(kNop), // should never get generated
+    TypeOpName(kAccumulatorPop),
+    TypeOpName(kAccumulatorPush),
+    TypeOpName(kAddInt),
+    TypeOpName(kAddScalar),
+    TypeOpName(kAddString), // string concat
+    TypeOpName(kArrayIndex),
+    TypeOpName(kArrayParam),
+    TypeOpName(kArrayToken),
+    TypeOpName(kBitAndInt),
+    TypeOpName(kBitNotInt),
+    TypeOpName(kBitOrInt),
+    TypeOpName(kBoxToken),
+    TypeOpName(kCallback),
+    TypeOpName(kDivideInt),
+    TypeOpName(kDivideScalar),
+    TypeOpName(kDotOperator),
+    TypeOpName(kElseOp),
+    TypeOpName(kEnd),
+    TypeOpName(kEqualInt),
+    TypeOpName(kEqualScalar),
+    TypeOpName(kEqualString),
+    TypeOpName(kFunctionCall),
+    TypeOpName(kFlipOpsOp),
+    TypeOpName(kFunctionToken),
+    TypeOpName(kGreaterEqualInt),
+    TypeOpName(kGreaterEqualScalar),
+    TypeOpName(kGreaterEqualString),
+    TypeOpName(kIfOp),
+    TypeOpName(kIntToScalar),
+    TypeOpName(kIntToScalar2),
+    TypeOpName(kIntToString),
+    TypeOpName(kIntToString2),
+    TypeOpName(kIntegerAccumulator),
+    TypeOpName(kIntegerOperand),
+    TypeOpName(kLogicalAndInt),
+    TypeOpName(kLogicalNotInt),
+    TypeOpName(kLogicalOrInt),
+    TypeOpName(kMemberOp),
+    TypeOpName(kMinusInt),
+    TypeOpName(kMinusScalar),
+    TypeOpName(kModuloInt),
+    TypeOpName(kModuloScalar),
+    TypeOpName(kMultiplyInt),
+    TypeOpName(kMultiplyScalar),
+    TypeOpName(kPropertyOp),
+    TypeOpName(kScalarAccumulator),
+    TypeOpName(kScalarOperand),
+    TypeOpName(kScalarToInt),
+    TypeOpName(kScalarToInt2),
+    TypeOpName(kScalarToString),
+    TypeOpName(kScalarToString2),
+    TypeOpName(kShiftLeftInt),
+    TypeOpName(kShiftRightInt), // signed
+    TypeOpName(kStringAccumulator),
+    TypeOpName(kStringOperand),
+    TypeOpName(kStringToInt),
+    TypeOpName(kStringToScalar),
+    TypeOpName(kStringToScalar2),
+    TypeOpName(kStringTrack),
+    TypeOpName(kSubtractInt),
+    TypeOpName(kSubtractScalar),
+    TypeOpName(kToBool),
+    TypeOpName(kUnboxToken),
+    TypeOpName(kUnboxToken2),
+    TypeOpName(kXorInt)
+};
+
+static size_t gOpNamesSize = sizeof(gOpNames) / sizeof(gOpNames[0]);
+
+#define OperandName(op) {SkOperand2::op, #op }
+
+static const struct OperName {
+    SkOperand2::OpType fType;
+    const char* fName;
+} gOperandNames[] = {
+    OperandName(kNoType),
+    OperandName(kS32),
+    OperandName(kScalar),
+    OperandName(kString),
+    OperandName(kArray),
+    OperandName(kObject)
+};  
+
+static size_t gOperandNamesSize = sizeof(gOperandNames) / sizeof(gOperandNames[0]);
+
+// check to see that there are no missing or duplicate entries
+void SkScriptEngine2::ValidateDecompileTable() {
+    SkScriptEngine2::TypeOp op = SkScriptEngine2::kNop;
+    size_t index;
+    for (index = 0; index < gOpNamesSize; index++) {
+        SkASSERT(gOpNames[index].fOp == op);
+        op = (SkScriptEngine2::TypeOp) (op + 1);
+    }
+    index = 0;
+    SkOperand2::OpType type = SkOperand2::kNoType;
+    SkASSERT(gOperandNames[index].fType == type);
+    for (; index < gOperandNamesSize - 1; ) {
+        type = (SkOperand2::OpType) (1 << index);
+        SkASSERT(gOperandNames[++index].fType == type);
+    }
+}
+
+void SkScriptEngine2::decompile(const unsigned char* start, size_t length) {
+    SkASSERT(length > 0);
+    const unsigned char* opCode = start;
+    do {
+        SkASSERT((size_t)(opCode - start) < length);
+        SkScriptEngine2::TypeOp op = (SkScriptEngine2::TypeOp) *opCode++;
+        SkASSERT((size_t)op < gOpNamesSize);
+        SkDebugf("%d: %s", opCode - start - 1, gOpNames[op].fName);
+        switch (op) {
+        case SkScriptEngine2::kCallback: {
+            int index;
+            memcpy(&index, opCode, sizeof(index));
+            opCode += sizeof(index);
+            SkDebugf(" index: %d", index);
+            } break;
+        case SkScriptEngine2::kFunctionCall: 
+        case SkScriptEngine2::kMemberOp:
+        case SkScriptEngine2::kPropertyOp: {
+            size_t ref;
+            memcpy(&ref, opCode, sizeof(ref));
+            opCode += sizeof(ref);
+            SkDebugf(" ref: %d", ref);
+            } break;
+        case SkScriptEngine2::kIntegerAccumulator:
+        case SkScriptEngine2::kIntegerOperand: {
+            int32_t integer;
+            memcpy(&integer, opCode, sizeof(integer));
+            opCode += sizeof(int32_t);
+            SkDebugf(" integer: %d", integer);
+            } break;
+        case SkScriptEngine2::kScalarAccumulator:
+        case SkScriptEngine2::kScalarOperand: {
+            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: {
+            int size;
+            SkString* strPtr = new SkString();
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            strPtr->set((char*) opCode, size);
+            opCode += size;
+            SkDebugf(" string: %s", strPtr->c_str());
+            delete strPtr;
+            } break;
+        case SkScriptEngine2::kBoxToken: {
+            SkOperand2::OpType type;
+            memcpy(&type, opCode, sizeof(type));
+            opCode += sizeof(type);
+            size_t index = 0;
+            if (type == 0)
+                SkDebugf(" type: %s", gOperandNames[index].fName);
+            else {
+                while (type != 0) {
+                    SkASSERT(index + 1 < gOperandNamesSize);
+                    if (type & (1 << index)) {
+                        type = (SkOperand2::OpType) (type & ~(1 << index));
+                        SkDebugf(" type: %s", gOperandNames[index + 1].fName);
+                    }
+                    index++;
+                }
+            }
+            } break;
+        case SkScriptEngine2::kIfOp:
+        case SkScriptEngine2::kLogicalAndInt:
+        case SkScriptEngine2::kElseOp:
+        case SkScriptEngine2::kLogicalOrInt: {
+            int size;
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            SkDebugf(" offset (address): %d (%d)", size, opCode - start + size);
+            } break;
+        case SkScriptEngine2::kEnd:
+            goto done;
+        case SkScriptEngine2::kNop:
+                SkASSERT(0);
+        default:
+            break;
+    }
+    SkDebugf("\n");
+    } while (true);
+done:
+    SkDebugf("\n");
+}
+
+#endif
diff --git a/legacy/src/animator/SkScriptRuntime.cpp b/legacy/src/animator/SkScriptRuntime.cpp
new file mode 100644
index 0000000..6a96633
--- /dev/null
+++ b/legacy/src/animator/SkScriptRuntime.cpp
@@ -0,0 +1,352 @@
+
+/*
+ * 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 "SkScriptRuntime.h"
+#include "SkScript2.h"
+#include "SkMath.h"
+#include "SkParse.h"
+#include "SkScriptCallBack.h"
+#include "SkString.h"
+#include "SkOpArray.h"
+
+// script tokenizer
+
+// turn text into token string
+// turn number literals into inline UTF8-style values
+// process operators to turn standard notation into stack notation
+
+// defer processing until the tokens can all be resolved
+// then, turn token strings into indices into the appropriate tables / dictionaries
+
+// consider: const evaluation?
+
+// 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
+
+// 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
+
+// !!! things to do
+	// 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;
+}
+
+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);
+    default:
+        break;
+	}
+	} while (true);
+done:
+	fRunStack.push(operand[0]);
+	return true;
+}
+
+bool SkScriptRuntime::getResult(SkOperand2* result) {
+	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(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;
+}
+
+void SkScriptRuntime::untrack(SkString* string) {
+	int index = fTrackString.find(string);
+	SkASSERT(index >= 0);
+	fTrackString.begin()[index] = NULL;
+}
+
diff --git a/legacy/src/animator/SkScriptRuntime.h b/legacy/src/animator/SkScriptRuntime.h
new file mode 100644
index 0000000..5de7b30
--- /dev/null
+++ b/legacy/src/animator/SkScriptRuntime.h
@@ -0,0 +1,50 @@
+
+/*
+ * 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 SkScriptRuntime_DEFINED
+#define SkScriptRuntime_DEFINED
+
+#include "SkOperand2.h"
+#include "SkTDArray_Experimental.h"
+#include "SkTDStack.h"
+
+class SkScriptCallBack;
+
+typedef SkLongArray(SkString*) SkTDStringArray; 
+typedef SkLongArray(SkScriptCallBack*) SkTDScriptCallBackArray; 
+
+class SkScriptRuntime {
+public:
+	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);
+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&);
+};
+
+#endif // SkScriptRuntime_DEFINED
\ No newline at end of file
diff --git a/legacy/src/animator/SkScriptTokenizer.cpp b/legacy/src/animator/SkScriptTokenizer.cpp
new file mode 100644
index 0000000..b2b6e4f
--- /dev/null
+++ b/legacy/src/animator/SkScriptTokenizer.cpp
@@ -0,0 +1,1525 @@
+
+/*
+ * 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 "SkScript2.h"
+#include "SkData.h"
+#include "SkFloatingPoint.h"
+#include "SkMath.h"
+#include "SkParse.h"
+#include "SkScriptCallBack.h"
+#include "SkScriptRuntime.h"
+#include "SkString.h"
+#include "SkOpArray.h"
+
+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), 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), kNoBias, kResultIsNotBoolean }, // kDivide
+{ 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), 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), kNoBias, kResultIsNotBoolean }, // kModulo
+{ 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), kNoBias, kResultIsNotBoolean }, // kSubtract
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean } // kXor
+};
+
+#define kBracketPrecedence 16
+#define kIfElsePrecedence 15
+
+const signed char SkScriptEngine2::gPrecedence[] = {
+    17, // kUnassigned,
+    6, // kAdd,
+    10, // kBitAnd,
+    4, // kBitNot,
+    12, // kBitOr,
+    5, // kDivide,
+    9, // kEqual,
+    -1, // kFlipOps,
+    8, // kGreaterEqual,
+    13, // kLogicalAnd,
+    4, // kLogicalNot,
+    14, // kLogicalOr,
+    4, // kMinus,
+    5, // kModulo,
+    5, // kMultiply,
+    7, // kShiftLeft,
+    7, // kShiftRight,    // signed
+    6, // kSubtract,
+    11, // kXor
+    kBracketPrecedence, // kArrayOp
+    kIfElsePrecedence, // kElse
+    kIfElsePrecedence, // kIf
+    kBracketPrecedence, // kParen
+};
+
+const SkScriptEngine2::TypeOp SkScriptEngine2::gTokens[] = {
+    kNop, // unassigned
+    kAddInt, // kAdd,
+    kBitAndInt, // kBitAnd,
+    kBitNotInt, // kBitNot,
+    kBitOrInt, // kBitOr,
+    kDivideInt, // kDivide,
+    kEqualInt, // kEqual,
+    kFlipOpsOp, // kFlipOps,
+    kGreaterEqualInt, // kGreaterEqual,
+    kLogicalAndInt, // kLogicalAnd,
+    kLogicalNotInt, // kLogicalNot,
+    kLogicalOrInt, // kLogicalOr,
+    kMinusInt, // kMinus,
+    kModuloInt, // kModulo,
+    kMultiplyInt, // kMultiply,
+    kShiftLeftInt, // kShiftLeft,
+    kShiftRightInt, // kShiftRight,    // signed
+    kSubtractInt, // kSubtract,
+    kXorInt // kXor
+};
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static int token_length(const char* start) {
+    char ch = start[0];
+    if (! is_between(ch, 'a' , 'z') &&  ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
+        return -1;
+    int length = 0;
+    do
+        ch = start[++length];
+    while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
+           ch == '_' || ch == '$');
+    return length;
+}
+
+SkScriptEngine2::SkScriptEngine2(SkOperand2::OpType returnType) : fActiveStream(&fStream),
+fTokenLength(0), fReturnType(returnType), fError(kNoError), 
+fAccumulatorType(SkOperand2::kNoType),
+fBranchPopAllowed(true), fConstExpression(true), fOperandInUse(false)
+{
+    Branch branch(kUnassigned, 0, 0);
+    fBranchStack.push(branch);
+    *fOpStack.push() = (Op) kParen;
+}
+
+SkScriptEngine2::~SkScriptEngine2() {
+    for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
+        delete *stringPtr;
+    for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
+        delete *arrayPtr;
+}
+
+void SkScriptEngine2::addToken(SkScriptEngine2::TypeOp op) {
+    int limit = fBranchStack.count() - 1;
+    for (int index = 0; index < limit; index++) {
+        Branch& branch = fBranchStack.index(index);
+        if (branch.fPrimed == Branch::kIsPrimed)
+            resolveBranch(branch);
+    }
+    if (fBranchPopAllowed) {
+        while (fBranchStack.top().fDone == Branch::kIsDone)
+            fBranchStack.pop();
+    }
+    unsigned char charOp = (unsigned char) op;
+    fActiveStream->write(&charOp, sizeof(charOp));
+}
+
+void SkScriptEngine2::addTokenConst(SkScriptValue2* value, AddTokenRegister reg, 
+                                    SkOperand2::OpType toType, SkScriptEngine2::TypeOp op) {
+    if (value->fIsConstant == SkScriptValue2::kConstant && convertTo(toType, value))
+        return;
+    addTokenValue(*value, reg);
+    addToken(op);
+    value->fIsWritten = SkScriptValue2::kWritten;
+    value->fType = toType;
+}
+
+void SkScriptEngine2::addTokenInt(int integer) {
+    fActiveStream->write(&integer, sizeof(integer));
+}
+
+void SkScriptEngine2::addTokenScalar(SkScalar scalar) {
+    fActiveStream->write(&scalar, sizeof(scalar));
+}
+
+void SkScriptEngine2::addTokenString(const SkString& string) {
+    int size = string.size();
+    addTokenInt(size);
+    fActiveStream->write(string.c_str(), size);
+}
+
+void SkScriptEngine2::addTokenValue(const SkScriptValue2& value, AddTokenRegister reg) {
+    if (value.isConstant() == false) {
+        if (reg == kAccumulator) {
+            if (fAccumulatorType == SkOperand2::kNoType)
+                addToken(kAccumulatorPop);
+        } else {
+            ; // !!! incomplete?
+        }
+        return;
+    }
+    if (reg == kAccumulator && fAccumulatorType != SkOperand2::kNoType)
+        addToken(kAccumulatorPush);
+    switch (value.fType) {
+        case SkOperand2::kS32:
+            addToken(reg == kAccumulator ? kIntegerAccumulator : kIntegerOperand);
+            addTokenInt(value.fOperand.fS32);
+            if (reg == kAccumulator)
+                fAccumulatorType = SkOperand2::kS32;
+            else
+                fOperandInUse = true;
+            break;
+        case SkOperand2::kScalar:
+            addToken(reg == kAccumulator ? kScalarAccumulator : kScalarOperand);
+            addTokenScalar(value.fOperand.fScalar);
+            if (reg == kAccumulator)
+                fAccumulatorType = SkOperand2::kScalar;
+            else
+                fOperandInUse = true;
+            break;
+        case SkOperand2::kString:
+            addToken(reg == kAccumulator ? kStringAccumulator : kStringOperand);
+            addTokenString(*value.fOperand.fString);
+            if (reg == kAccumulator)
+                fAccumulatorType = SkOperand2::kString;
+            else
+                fOperandInUse = true;
+            break;
+        default:
+            SkASSERT(0); //!!! not implemented yet
+    }
+}
+
+int SkScriptEngine2::arithmeticOp(char ch, char nextChar, bool lastPush) {
+    Op op = kUnassigned;
+    bool reverseOperands = false;
+    bool negateResult = false;
+    int advance = 1;
+    switch (ch) {
+        case '+':
+            // !!! ignoring unary plus as implemented here has the side effect of
+            // suppressing errors like +"hi"
+            if (lastPush == false)    // unary plus, don't push an operator
+                return advance;
+            op = kAdd;
+            break;
+        case '-':
+            op = lastPush ? kSubtract : kMinus;
+            break;
+        case '*':
+            op = kMultiply;
+            break;
+        case '/':
+            op = kDivide;
+            break;
+        case '>':
+            if (nextChar == '>') {
+                op = kShiftRight;
+                goto twoChar;
+            } 
+            op = kGreaterEqual;
+            if (nextChar == '=')
+                goto twoChar;
+                reverseOperands = negateResult = true;
+            break;
+        case '<':
+            if (nextChar == '<') {
+                op = kShiftLeft;
+                goto twoChar;
+            }
+            op = kGreaterEqual;
+            reverseOperands = nextChar == '=';
+            negateResult = ! reverseOperands;
+            advance += reverseOperands;
+            break;
+        case '=':
+            if (nextChar == '=') {
+                op = kEqual;
+                goto twoChar;
+            }
+            break;
+        case '!':
+            if (nextChar == '=') {
+                op = kEqual;
+                negateResult = true;
+twoChar:
+                    advance++;
+                break;
+            } 
+            op = kLogicalNot;
+            break;
+        case '?':
+            op =(Op)  kIf;
+            break;
+        case ':':
+            op = (Op) kElse;
+            break;
+        case '^':
+            op = kXor;
+            break;
+        case '(':
+            *fOpStack.push() = (Op) kParen;
+            return advance;
+        case '&':
+            SkASSERT(nextChar != '&');
+            op = kBitAnd;
+            break;
+        case '|':
+            SkASSERT(nextChar != '|');
+            op = kBitOr;
+            break;
+        case '%':
+            op = kModulo;
+            break;
+        case '~':
+            op = kBitNot;
+            break;
+    }
+    if (op == kUnassigned)
+        return 0;
+    signed char precedence = gPrecedence[op];
+    do {
+        int idx = 0;
+        Op compare;
+        do {
+            compare = fOpStack.index(idx);
+            if ((compare & kArtificialOp) == 0)
+                break;
+            idx++;
+        } while (true);
+        signed char topPrecedence = gPrecedence[compare];
+        SkASSERT(topPrecedence != -1);
+        if (topPrecedence > precedence || (topPrecedence == precedence && 
+            gOpAttributes[op].fLeftType == SkOperand2::kNoType)) {
+            break;
+        }
+        processOp();
+    } while (true);
+    if (negateResult)
+        *fOpStack.push() = (Op) (kLogicalNot | kArtificialOp);
+    fOpStack.push(op);
+    if (reverseOperands)
+        *fOpStack.push() = (Op) (kFlipOps | kArtificialOp);
+
+    return advance;
+}
+
+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++) 
+        convertTo(paramTypes[index], &(*params)[index]);
+    return true;
+}
+
+bool SkScriptEngine2::convertTo(SkOperand2::OpType toType, SkScriptValue2* value ) {
+    SkOperand2::OpType type = value->fType;
+    if (type == toType)
+        return true;
+    if (type == SkOperand2::kObject) {
+        if (handleUnbox(value) == false)
+            return false;
+        return convertTo(toType, value);
+    }
+    return ConvertTo(this, toType, value);
+}
+
+bool SkScriptEngine2::evaluateDot(const char*& script) { 
+    size_t fieldLength = token_length(++script);        // skip dot
+    SkASSERT(fieldLength > 0); // !!! add error handling
+    const char* field = script;
+    script += fieldLength;
+    bool success = handleProperty();
+    if (success == false) {
+        fError = kCouldNotFindReferencedID;
+        goto error;
+    }
+    return evaluateDotParam(script, field, fieldLength);
+error:
+        return false;
+}
+
+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])) 
+        script++;
+    bool success = true;
+    if (ch != '(')
+        success = handleMember(field, fieldLength, object);
+    else {
+        SkTDArray<SkScriptValue2> params;
+        *fBraceStack.push() = kFunctionBrace;
+        success = functionParams(&script, &params);
+        if (success)
+            success = handleMemberFunction(field, fieldLength, object, &params);
+    }
+    return success; 
+}
+
+bool SkScriptEngine2::evaluateScript(const char** scriptPtr, SkScriptValue2* value) {
+    //    fArrayOffset = 0;        // no support for structures for now
+    bool success;
+    const char* inner;
+    if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
+        *scriptPtr += sizeof("#script:") - 1;
+        if (fReturnType == SkOperand2::kNoType || fReturnType == SkOperand2::kString) {
+            success = innerScript(scriptPtr, value);
+            SkASSERT(success);
+            inner = value->fOperand.fString->c_str();
+            scriptPtr = &inner;
+        }
+    }
+    success = innerScript(scriptPtr, value);
+    const char* script = *scriptPtr;
+    char ch;
+    while (is_ws(ch = script[0]))
+        script++;
+    if (ch != '\0') {
+        // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
+        return false;
+    }
+    return success;
+}
+
+void SkScriptEngine2::forget(SkOpArray* array) {
+    if (array->getType() == SkOperand2::kString) {
+        for (int index = 0; index < array->count(); index++) {
+            SkString* string = (*array)[index].fString;
+            int found = fTrackString.find(string);
+            if (found >= 0)
+                fTrackString.remove(found);
+        }
+        return;
+    }
+    if (array->getType() == SkOperand2::kArray) {
+        for (int index = 0; index < array->count(); index++) {
+            SkOpArray* child = (*array)[index].fArray;
+            forget(child);    // forgets children of child
+            int found = fTrackArray.find(child);
+            if (found >= 0)
+                fTrackArray.remove(found);
+        }
+    }
+}
+
+bool SkScriptEngine2::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params) {
+    (*scriptPtr)++; // skip open paren
+    *fOpStack.push() = (Op) kParen;
+    *fBraceStack.push() = kFunctionBrace;
+    do {
+        SkScriptValue2 value;
+        bool success = innerScript(scriptPtr, &value);
+        SkASSERT(success);
+        if (success == false)
+            return false;
+        *params->append() = value;
+    } while ((*scriptPtr)[-1] == ',');
+    fBraceStack.pop();
+    fOpStack.pop(); // pop paren
+    (*scriptPtr)++; // advance beyond close paren
+    return true;
+}
+
+size_t SkScriptEngine2::getTokenOffset() {
+    return fActiveStream->getOffset();
+}
+
+SkOperand2::OpType SkScriptEngine2::getUnboxType(SkOperand2 scriptValue) {
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
+            continue;
+        return (*callBack)->getReturnType(0, &scriptValue);
+    }
+    return SkOperand2::kObject;
+}
+
+bool SkScriptEngine2::innerScript(const char** scriptPtr, SkScriptValue2* value) {
+    const char* script = *scriptPtr;
+    char ch;
+    bool lastPush = false;
+    bool success = true;
+    int opBalance = fOpStack.count();
+    int baseBrace = fBraceStack.count();
+    int branchBalance = fBranchStack.count();
+    while ((ch = script[0]) != '\0') {
+        if (is_ws(ch)) {
+            script++;
+            continue;
+        }
+        SkScriptValue2 operand;
+        const char* dotCheck;
+        if (fBraceStack.count() > baseBrace) {
+            if (fBraceStack.top() == kArrayBrace) {
+                SkScriptValue2 tokenValue;
+                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
+                SkASSERT(success);
+                {
+                    SkOperand2::OpType type = fReturnType;
+                    if (fReturnType == SkOperand2::kNoType) {
+                        // !!! 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);
+                        else
+                            type = value->fOperand.fArray->getType();
+                    }
+                    if (tokenValue.fType != type)
+                        convertTo(type, &tokenValue);
+                    *value->fOperand.fArray->append() = tokenValue.fOperand;
+                }
+                lastPush = false;
+                continue;
+            } else {
+                SkASSERT(token_length(script) > 0);
+            }
+        }
+        if (lastPush != false && fTokenLength > 0) {
+            if (ch == '(') {
+                *fBraceStack.push() = kFunctionBrace;
+                SkString functionName(fToken, fTokenLength);
+                
+                if (handleFunction(&script) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch == '[') {
+                if (handleProperty() == false) {
+                    SkASSERT(0);
+                    return false;
+                }
+                if (handleArrayIndexer(&script) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch != '.') {
+                if (handleProperty() == false) {
+                    SkASSERT(0);
+                    return false;
+                }
+                lastPush = true;
+                continue;
+            }
+        }
+        if (ch == '0' && (script[1] & ~0x20) == 'X') {
+            SkASSERT(lastPush == false);
+            script += 2;
+            script = SkParse::FindHex(script, (uint32_t*) &operand.fOperand.fS32);
+            SkASSERT(script);
+            goto intCommon;
+        }
+        if (lastPush == false && ch == '.')
+            goto scalarCommon;
+        if (ch >= '0' && ch <= '9') {
+            SkASSERT(lastPush == false);
+            dotCheck = SkParse::FindS32(script, &operand.fOperand.fS32);
+            if (dotCheck[0] != '.') {
+                script = dotCheck;
+intCommon:
+                operand.fType = SkOperand2::kS32;
+            } else {
+scalarCommon:
+                script = SkParse::FindScalar(script, &operand.fOperand.fScalar);
+                operand.fType = SkOperand2::kScalar;
+            }
+            operand.fIsConstant = SkScriptValue2::kConstant;
+            fValueStack.push(operand);
+            lastPush = true;
+            continue;
+        }
+        int length = token_length(script);
+        if (length > 0) {
+            SkASSERT(lastPush == false);
+            fToken = script;
+            fTokenLength = length;
+            script += length;
+            lastPush = true;
+            continue;
+        }
+        char startQuote = ch;
+        if (startQuote == '\'' || startQuote == '\"') {
+            SkASSERT(lastPush == false);
+            operand.fOperand.fString = new SkString();
+            ++script;
+            const char* stringStart = script;
+            do {    // measure string
+                if (script[0] == '\\')
+                    ++script;
+                ++script;
+                SkASSERT(script[0]); // !!! throw an error
+            } while (script[0] != startQuote);
+            operand.fOperand.fString->set(stringStart, script - stringStart);
+            script = stringStart;
+            char* stringWrite = operand.fOperand.fString->writable_str();
+            do {    // copy string
+                if (script[0] == '\\')
+                    ++script;
+                *stringWrite++ = script[0];
+                ++script;
+                SkASSERT(script[0]); // !!! throw an error
+            } while (script[0] != startQuote);
+            ++script;
+            track(operand.fOperand.fString);
+            operand.fType = SkOperand2::kString;
+            operand.fIsConstant = SkScriptValue2::kConstant;
+            fValueStack.push(operand);
+            lastPush = true;
+            continue;
+        }
+        if (ch ==  '.') {
+            if (fTokenLength == 0) {
+                SkDEBUGCODE(SkScriptValue2 scriptValue;)
+                SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
+                int tokenLength = token_length(++script);
+                const char* token = script;
+                script += tokenLength;
+                SkASSERT(fValueStack.count() > 0); // !!! add error handling
+                SkScriptValue2 top;
+                fValueStack.pop(&top);
+                
+                addTokenInt(top.fType);
+                addToken(kBoxToken);
+                top.fType = SkOperand2::kObject;
+                top.fIsConstant = SkScriptValue2::kVariable;
+                fConstExpression = false;
+                fValueStack.push(top);
+                success = evaluateDotParam(script, token, tokenLength);
+                SkASSERT(success);
+                lastPush = true;
+                continue; 
+            }
+            // get next token, and evaluate immediately
+            success = evaluateDot(script);
+            if (success == false) {
+                //                SkASSERT(0);
+                return false;
+            }
+            lastPush = true;
+            continue;
+        }
+        if (ch == '[') {
+            if (lastPush == false) {
+                script++;
+                *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);
+                continue;
+            }
+            if (handleArrayIndexer(&script) == false)
+                return false;
+            lastPush = true;
+            continue;
+        }
+#if 0 // structs not supported for now
+        if (ch == '{') {
+            if (lastPush == false) {
+                script++;
+                *fBraceStack.push() = kStructBrace;
+                operand.fS32 = 0;
+                *fTypeStack.push() = (SkOpType) kStruct;
+                fOperandStack.push(operand);
+                continue;
+            }
+            SkASSERT(0); // braces in other contexts aren't supported yet
+        }
+#endif
+        if (ch == ')' && fBraceStack.count() > 0) {
+            BraceStyle braceStyle = fBraceStack.top(); 
+            if (braceStyle == kFunctionBrace) {
+                fBraceStack.pop();
+                break;
+            }
+        }
+        if (ch == ',' || ch == ']') {
+            if (ch != ',') {
+                BraceStyle match;
+                fBraceStack.pop(&match);
+                SkASSERT(match == kArrayBrace);
+            }
+            script++;
+            // !!! see if brace or bracket is correct closer
+            break;
+        }
+        char nextChar = script[1];
+        int advance = logicalOp(ch, nextChar);
+        if (advance == 0) 
+            advance = arithmeticOp(ch, nextChar, lastPush);
+        if (advance == 0) // unknown token
+            return false;
+        if (advance > 0)
+            script += advance;
+        lastPush = ch == ']' || ch == ')';
+    }
+    if (fTokenLength > 0) {
+        success = handleProperty();
+        SkASSERT(success);
+    }
+    int branchIndex = 0;
+    branchBalance = fBranchStack.count() - branchBalance;
+    fBranchPopAllowed = false;
+    while (branchIndex < branchBalance) {
+        Branch& branch = fBranchStack.index(branchIndex++);
+        if (branch.fPrimed == Branch::kIsPrimed)
+            break;
+        Op branchOp = branch.fOperator;
+        SkOperand2::OpType lastType = fValueStack.top().fType;
+        addTokenValue(fValueStack.top(), kAccumulator);
+        fValueStack.pop();
+        if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
+            if (branch.fOperator == kLogicalAnd)
+                branch.prime();
+            addToken(kToBool);
+        } else {
+            resolveBranch(branch);
+            SkScriptValue2 operand;
+            operand.fType = lastType;
+            // !!! note that many branching expressions could be constant
+            // today, we always evaluate branches as returning variables
+            operand.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(operand);
+        }
+        if (branch.fDone == Branch::kIsNotDone)
+            branch.prime();
+    }
+    fBranchPopAllowed = true;
+    while (fBranchStack.top().fDone == Branch::kIsDone)
+        fBranchStack.pop();
+    while (fOpStack.count() > opBalance) {     // leave open paren
+        if (processOp() == false)
+            return false;
+    }
+    SkOperand2::OpType topType = fValueStack.count() > 0 ? fValueStack.top().fType : SkOperand2::kNoType;
+    if (topType != fReturnType &&
+        topType == SkOperand2::kString && fReturnType != SkOperand2::kNoType) { // if result is a string, give handle property a chance to convert it to the property value
+        SkString* string = fValueStack.top().fOperand.fString;
+        fToken = string->c_str();
+        fTokenLength = string->size();
+        fValueStack.pop();
+        success = handleProperty();
+        if (success == false) {    // if it couldn't convert, return string (error?)
+            SkScriptValue2 operand;
+            operand.fType = SkOperand2::kString;
+            operand.fOperand.fString = string;
+            operand.fIsConstant = SkScriptValue2::kVariable;     // !!! ?
+            fValueStack.push(operand);
+        }
+    }
+    if (fStream.getOffset() > 0) {
+        addToken(kEnd);
+        SkAutoDataUnref data(fStream.copyToData());
+#ifdef SK_DEBUG
+        decompile(data.bytes(), data.size());
+#endif
+        SkScriptRuntime runtime(fCallBackArray);
+        runtime.executeTokens((unsigned char*) data.bytes());
+        SkScriptValue2 value1;
+        runtime.getResult(&value1.fOperand);
+        value1.fType = fReturnType;
+        fValueStack.push(value1);
+    }
+    if (value) {
+        if (fValueStack.count() == 0)
+            return false;
+        fValueStack.pop(value);
+        if (value->fType != fReturnType && value->fType == SkOperand2::kObject && 
+            fReturnType != SkOperand2::kNoType)
+            convertTo(fReturnType, value);
+    }
+    //    if (fBranchStack.top().fOpStackDepth > fOpStack.count())
+    //        resolveBranch();
+    *scriptPtr = script;
+    return true; // no error
+}
+
+bool SkScriptEngine2::handleArrayIndexer(const char** scriptPtr) {
+    SkScriptValue2 scriptValue;
+    (*scriptPtr)++;
+    *fOpStack.push() = (Op) kParen;
+    *fBraceStack.push() = kArrayBrace;
+    SkOperand2::OpType saveType = fReturnType;
+    fReturnType = SkOperand2::kS32;
+    bool success = innerScript(scriptPtr, &scriptValue);
+    fReturnType = saveType;
+    SkASSERT(success);
+    success = convertTo(SkOperand2::kS32, &scriptValue);
+    SkASSERT(success);
+    int index = scriptValue.fOperand.fS32;
+    fValueStack.pop(&scriptValue);
+    if (scriptValue.fType == SkOperand2::kObject) {
+        success = handleUnbox(&scriptValue);
+        SkASSERT(success);
+        SkASSERT(scriptValue.fType == SkOperand2::kArray);
+    }
+    scriptValue.fType = scriptValue.fOperand.fArray->getType();
+    //    SkASSERT(index >= 0);
+    if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
+        fError = kArrayIndexOutOfBounds;
+        return false;
+    }
+    scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
+    scriptValue.fIsConstant = SkScriptValue2::kVariable;
+    fValueStack.push(scriptValue);
+    fOpStack.pop(); // pop paren
+    return success;
+}
+
+bool SkScriptEngine2::handleFunction(const char** scriptPtr) {
+    const char* functionName = fToken;
+    size_t functionNameLen = fTokenLength;
+    fTokenLength = 0;
+    SkTDArray<SkScriptValue2> params;
+    bool success = functionParams(scriptPtr, &params);
+    if (success == false)
+        goto done;
+    {
+        for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+            if ((*callBack)->getType() != SkScriptCallBack::kFunction)
+                continue;
+            SkScriptValue2 callbackResult;
+            success = (*callBack)->getReference(functionName, functionNameLen, &callbackResult);
+            if (success) {
+                callbackResult.fType = (*callBack)->getReturnType(callbackResult.fOperand.fReference, NULL);
+                callbackResult.fIsConstant = SkScriptValue2::kVariable;
+                fValueStack.push(callbackResult);
+                goto done;
+            }
+        }
+    }
+    return false;
+done:
+        fOpStack.pop();
+    return success;
+}
+
+bool SkScriptEngine2::handleMember(const char* field, size_t len, void* object) {
+    bool success = true;
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kMember)
+            continue;
+        SkScriptValue2 callbackResult;
+        success = (*callBack)->getReference(field, len, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkOperand2::kString)
+                track(callbackResult.fOperand.fString);
+            callbackResult.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(callbackResult);
+            goto done;
+        }
+    }
+    return false;
+done:
+        return success;
+}
+
+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++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kMemberFunction)
+            continue;
+        SkScriptValue2 callbackResult;
+        success = (*callBack)->getReference(field, len, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkOperand2::kString)
+                track(callbackResult.fOperand.fString);
+            callbackResult.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(callbackResult);
+            goto done;
+        }
+    }
+    return false;
+done:
+        return success;
+}
+
+bool SkScriptEngine2::handleProperty() {
+    bool success = true;
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kProperty)
+            continue;
+        SkScriptValue2 callbackResult;
+        success = (*callBack)->getReference(fToken, fTokenLength, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkOperand2::kString && callbackResult.fOperand.fString == NULL) {
+                callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
+                track(callbackResult.fOperand.fString);
+            }
+            callbackResult.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(callbackResult);
+            goto done;
+        }
+    }
+done:
+        fTokenLength = 0;
+    return success;
+}
+
+bool SkScriptEngine2::handleUnbox(SkScriptValue2* scriptValue) {
+    bool success = true;
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
+            continue;
+        SkScriptCallBackConvert* callBackConvert = (SkScriptCallBackConvert*) *callBack;
+        success = callBackConvert->convert(scriptValue->fType, &scriptValue->fOperand);
+        if (success) {
+            if (scriptValue->fType == SkOperand2::kString)
+                track(scriptValue->fOperand.fString);
+            goto done;
+        }
+    }
+    return false;
+done:
+        return success;
+}
+
+// note that entire expression is treated as if it were enclosed in parens
+// an open paren is always the first thing in the op stack
+
+int SkScriptEngine2::logicalOp(char ch, char nextChar) {
+    int advance = 1;
+    Op op;
+    signed char precedence;
+    switch (ch) {
+        case ')':
+            op = (Op) kParen;
+            break;
+        case ']':
+            op = (Op) kArrayOp;
+            break;
+        case '?':
+            op = (Op) kIf;
+            break;
+        case ':':
+            op = (Op) kElse;
+            break;
+        case '&':
+            if (nextChar != '&')
+                goto noMatch;
+            op = kLogicalAnd;
+            advance = 2;
+            break;
+        case '|':
+            if (nextChar != '|')
+                goto noMatch;
+            op = kLogicalOr;
+            advance = 2;
+            break;
+        default:
+            noMatch:
+            return 0;
+    }
+    precedence = gPrecedence[op];
+    int branchIndex = 0;
+    fBranchPopAllowed = false;
+    do {
+        while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence)
+            processOp();
+        Branch& branch = fBranchStack.index(branchIndex++);
+        Op branchOp = branch.fOperator;
+        if (gPrecedence[branchOp] >= precedence)
+            break;
+        addTokenValue(fValueStack.top(), kAccumulator);
+        fValueStack.pop();
+        if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
+            if (branch.fOperator == kLogicalAnd)
+                branch.prime();
+            addToken(kToBool);
+        } else
+            resolveBranch(branch);
+        if (branch.fDone == Branch::kIsNotDone)
+            branch.prime();
+    } while (true);
+    fBranchPopAllowed = true;
+    while (fBranchStack.top().fDone == Branch::kIsDone)
+        fBranchStack.pop();
+    processLogicalOp(op);
+    return advance;
+}
+
+void SkScriptEngine2::processLogicalOp(Op op) {
+    switch (op) {
+        case kParen:
+        case kArrayOp:
+            SkASSERT(fOpStack.count() > 1 && fOpStack.top() == op);    // !!! add error handling
+            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) : 
+                    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);
+                SkASSERT(success); // !!! add error handling
+                SkScriptValue2 resultValue;
+                resultValue.fType = array->getType();
+                resultValue.fOperand = operand;
+                resultValue.fIsConstant = SkScriptValue2::kVariable;
+                fValueStack.push(resultValue);
+            }
+                break;
+        case kIf: {
+            if (fAccumulatorType == SkOperand2::kNoType) {
+                addTokenValue(fValueStack.top(), kAccumulator);
+                fValueStack.pop();
+            }
+            SkASSERT(fAccumulatorType != SkOperand2::kString); // !!! add error handling
+            addToken(kIfOp);
+            Branch branch(op, fOpStack.count(), getTokenOffset());
+            *fBranchStack.push() = branch;
+            addTokenInt(0); // placeholder for future branch
+            fAccumulatorType = SkOperand2::kNoType;
+        } break;
+        case kElse: {
+            addTokenValue(fValueStack.top(), kAccumulator);
+            fValueStack.pop();
+            addToken(kElseOp);
+            size_t newOffset = getTokenOffset();
+            addTokenInt(0); // placeholder for future branch
+            Branch& branch = fBranchStack.top();
+            resolveBranch(branch);
+            branch.fOperator = op;
+            branch.fDone = Branch::kIsNotDone;
+            SkASSERT(branch.fOpStackDepth == fOpStack.count());
+            branch.fOffset = newOffset;
+            fAccumulatorType = SkOperand2::kNoType;
+        } break;
+        case kLogicalAnd:
+        case kLogicalOr: {
+            Branch& oldTop = fBranchStack.top();
+            Branch::Primed wasPrime = oldTop.fPrimed;
+            Branch::Done wasDone = oldTop.fDone;
+            oldTop.fPrimed = Branch::kIsNotPrimed;
+            oldTop.fDone = Branch::kIsNotDone;
+            if (fAccumulatorType == SkOperand2::kNoType) {
+                SkASSERT(fValueStack.top().fType == SkOperand2::kS32); // !!! add error handling, and conversion to int?
+                addTokenValue(fValueStack.top(), kAccumulator);
+                fValueStack.pop();
+            } else {
+                SkASSERT(fAccumulatorType == SkOperand2::kS32);
+            }
+            // if 'and', write beq goto opcode after end of predicate (after to bool)
+            // if 'or', write bne goto to bool
+            addToken(op == kLogicalAnd ? kLogicalAndInt : kLogicalOrInt);
+            Branch branch(op, fOpStack.count(), getTokenOffset());
+            addTokenInt(0); // placeholder for future branch            
+            oldTop.fPrimed = wasPrime;
+            oldTop.fDone = wasDone;
+            *fBranchStack.push() = branch;
+            fAccumulatorType = SkOperand2::kNoType;
+        }    break;
+        default:
+            SkASSERT(0);
+    }
+}
+
+bool SkScriptEngine2::processOp() {
+    Op op;
+    fOpStack.pop(&op);
+    op = (Op) (op & ~kArtificialOp);
+    const OperatorAttributes* attributes = &gOpAttributes[op];
+    SkScriptValue2 value1;
+    memset(&value1, 0, sizeof(SkScriptValue2));
+    SkScriptValue2 value2;
+    fValueStack.pop(&value2);
+    value2.fIsWritten = SkScriptValue2::kUnwritten;
+    //    SkScriptEngine2::SkTypeOp convert1[3];
+    //    SkScriptEngine2::SkTypeOp convert2[3];
+    //    SkScriptEngine2::SkTypeOp* convert2Ptr = convert2;
+    bool constantOperands = value2.fIsConstant == SkScriptValue2::kConstant;
+    if (attributes->fLeftType != SkOperand2::kNoType) {
+        fValueStack.pop(&value1);
+        constantOperands &= value1.fIsConstant == SkScriptValue2::kConstant; 
+        value1.fIsWritten = SkScriptValue2::kUnwritten;
+        if (op == kFlipOps) {
+            SkTSwap(value1, value2);
+            fOpStack.pop(&op);
+            op = (Op) (op & ~kArtificialOp);
+            attributes = &gOpAttributes[op];
+            if (constantOperands == false)
+                addToken(kFlipOpsOp);
+        }
+        if (value1.fType == SkOperand2::kObject && (value1.fType & attributes->fLeftType) == 0) {
+            value1.fType = getUnboxType(value1.fOperand);
+            addToken(kUnboxToken);
+        }
+    }
+    if (value2.fType == SkOperand2::kObject && (value2.fType & attributes->fLeftType) == 0) {
+        value1.fType = getUnboxType(value2.fOperand);
+        addToken(kUnboxToken2);
+    }
+    if (attributes->fLeftType != SkOperand2::kNoType) {
+        if (value1.fType != value2.fType) {
+            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, 
+                                  value1.fType == SkOperand2::kS32 ? kIntToString : kScalarToString);
+                }
+                if (value2.fType == SkOperand2::kS32 || value2.fType == SkOperand2::kScalar) {
+                    addTokenConst(&value2, kOperand, SkOperand2::kString, 
+                                  value2.fType == SkOperand2::kS32 ? kIntToString2 : kScalarToString2);
+                }
+            } else if (attributes->fLeftType & SkOperand2::kScalar && ((value1.fType | value2.fType) & 
+                                                                       SkOperand2::kScalar)) {
+                if (value1.fType == SkOperand2::kS32)
+                    addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kIntToScalar);
+                if (value2.fType == SkOperand2::kS32)
+                    addTokenConst(&value2, kOperand, SkOperand2::kScalar, kIntToScalar2);
+            }
+        }
+        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 || 
+                                                        value2.fType == SkOperand2::kS32))
+                addTokenConst(&value1, kAccumulator, SkOperand2::kS32, kScalarToInt);
+        }
+    }
+    AddTokenRegister rhRegister = attributes->fLeftType != SkOperand2::kNoType ?
+        kOperand : kAccumulator;
+    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 || 
+                                                    value1.fType == SkOperand2::kS32))
+            addTokenConst(&value2, rhRegister, SkOperand2::kS32, kScalarToInt2);
+    }
+    TypeOp typeOp = gTokens[op];
+    if (value2.fType == SkOperand2::kScalar)
+        typeOp = (TypeOp) (typeOp + 1);
+    else if (value2.fType == SkOperand2::kString)
+        typeOp = (TypeOp) (typeOp + 2);
+    SkDynamicMemoryWStream stream;
+    SkOperand2::OpType saveType = SkOperand2::kNoType;
+    SkBool saveOperand = false;
+    if (constantOperands) {
+        fActiveStream = &stream;
+        saveType = fAccumulatorType;
+        saveOperand = fOperandInUse;
+        fAccumulatorType = SkOperand2::kNoType;
+        fOperandInUse = false;
+    }
+    if (attributes->fLeftType != SkOperand2::kNoType) {    // two operands
+        if (value1.fIsWritten == SkScriptValue2::kUnwritten)
+            addTokenValue(value1, kAccumulator);
+    }
+    if (value2.fIsWritten == SkScriptValue2::kUnwritten)
+        addTokenValue(value2, rhRegister);
+    addToken(typeOp);
+    if (constantOperands) {
+        addToken(kEnd);
+        SkAutoDataUnref data(fStream.copyToData());
+#ifdef SK_DEBUG        
+        decompile(data.bytes(), data.size());
+#endif
+        SkScriptRuntime runtime(fCallBackArray);
+        runtime.executeTokens((unsigned char*)data.bytes());
+        runtime.getResult(&value1.fOperand);
+        if (attributes->fResultIsBoolean == kResultIsBoolean)
+            value1.fType = SkOperand2::kS32;
+        else if (attributes->fLeftType == SkOperand2::kNoType) // unary operand
+            value1.fType = value2.fType;
+        fValueStack.push(value1);
+        if (value1.fType == SkOperand2::kString)
+            runtime.untrack(value1.fOperand.fString);
+        else if (value1.fType == SkOperand2::kArray)
+            runtime.untrack(value1.fOperand.fArray);
+        fActiveStream = &fStream;
+        fAccumulatorType = saveType;
+        fOperandInUse = saveOperand;
+        return true;
+    }
+    value2.fIsConstant = SkScriptValue2::kVariable;
+    fValueStack.push(value2);
+    return true;
+}
+
+void SkScriptEngine2::Branch::resolve(SkDynamicMemoryWStream* stream, size_t off) {
+    SkASSERT(fDone == kIsNotDone);
+    fPrimed = kIsNotPrimed;
+    fDone = kIsDone;
+    SkASSERT(off > fOffset + sizeof(size_t));
+    size_t offset = off - fOffset - sizeof(offset);
+    stream->write(&offset, fOffset, sizeof(offset));
+}
+
+void SkScriptEngine2::resolveBranch(SkScriptEngine2::Branch& branch) {
+    branch.resolve(fActiveStream, getTokenOffset());
+}
+
+bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toType, SkScriptValue2* value ) {
+    SkASSERT(value);
+    SkOperand2::OpType type = value->fType;
+    if (type == toType) 
+        return true;
+    SkOperand2& operand = value->fOperand;
+    bool success = true;
+    switch (toType) {
+        case SkOperand2::kS32:
+            if (type == SkOperand2::kScalar)
+                operand.fS32 = SkScalarFloor(operand.fScalar);
+            else {
+                SkASSERT(type == SkOperand2::kString);
+                success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
+            }
+                break;
+        case SkOperand2::kScalar:
+            if (type == SkOperand2::kS32)
+                operand.fScalar = IntToScalar(operand.fS32);
+            else {
+                SkASSERT(type == SkOperand2::kString);
+                success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
+            }
+                break;
+        case SkOperand2::kString: {
+            SkString* strPtr = new SkString();
+            SkASSERT(engine);
+            engine->track(strPtr);
+            if (type == SkOperand2::kS32)
+                strPtr->appendS32(operand.fS32);
+            else {
+                SkASSERT(type == SkOperand2::kScalar);
+                strPtr->appendScalar(operand.fScalar);
+            }
+            operand.fString = strPtr;
+        } break;
+        case SkOperand2::kArray: {
+            SkOpArray* array = new SkOpArray(type);
+            *array->append() = operand;
+            engine->track(array);
+            operand.fArray = array;
+        } break;
+        default:
+            SkASSERT(0);
+    }
+    value->fType = toType;
+    return success;
+}
+
+SkScalar SkScriptEngine2::IntToScalar(int32_t s32) {
+    SkScalar scalar;
+    if (s32 == (int32_t) SK_NaN32)
+        scalar = SK_ScalarNaN;
+    else if (SkAbs32(s32) == SK_MaxS32)
+        scalar = SkSign32(s32) * SK_ScalarMax;
+    else
+        scalar = SkIntToScalar(s32);
+    return scalar;
+}
+
+bool SkScriptEngine2::ValueToString(const SkScriptValue2& value, SkString* string) {
+    switch (value.fType) {
+        case SkOperand2::kS32:
+            string->reset();
+            string->appendS32(value.fOperand.fS32);
+            break;
+        case SkOperand2::kScalar:
+            string->reset();
+            string->appendScalar(value.fOperand.fScalar);
+            break;
+        case SkOperand2::kString:
+            string->set(*value.fOperand.fString);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true; // no error
+}
+
+#ifdef SK_DEBUG
+
+#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 }
+#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
+    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), 
+    testRemainder(9,2.5),
+    testRemainder(-9,2.5),
+    testTrue(-9==-9.0),
+    testTrue(-9.==-4.0-5),
+    testTrue(-9.*1==-4-5),
+    testFalse(-9!=-9.0),
+    testFalse(-9.!=-4.0-5),
+    testFalse(-9.*1!=-4-5),
+#endif
+    testInt(0x123),
+    testInt(0XABC),
+    testInt(0xdeadBEEF),
+    {    "'123'+\"456\"", SkOperand2::kString, 0, 0, "123456" },
+    {    "123+\"456\"", SkOperand2::kString, 0, 0, "123456" },
+    {    "'123'+456", SkOperand2::kString, 0, 0, "123456" },
+    {    "'123'|\"456\"", SkOperand2::kS32, 123|456, 0, NULL },
+    {    "123|\"456\"", SkOperand2::kS32, 123|456, 0, NULL },
+    {    "'123'|456", SkOperand2::kS32, 123|456, 0, NULL },
+    {    "'2'<11", SkOperand2::kS32, 1, 0, NULL },
+    {    "2<'11'", SkOperand2::kS32, 1, 0, NULL },
+    {    "'2'<'11'", SkOperand2::kS32, 0, 0, NULL },
+    testInt(123),
+    testInt(-345),
+    testInt(+678),
+    testInt(1+2+3),
+    testInt(3*4+5),
+    testInt(6+7*8),
+    testInt(-1-2-8/4),
+    testInt(-9%4),
+    testInt(9%-4),
+    testInt(-9%-4),
+    testInt(123|978),
+    testInt(123&978),
+    testInt(123^978),
+    testInt(2<<4),
+    testInt(99>>3),
+    testInt(~55),
+    testInt(~~55),
+    testInt(!55),
+    testInt(!!55),
+    // both int
+    testInt(2<2),
+    testInt(2<11),
+    testInt(20<11),
+    testInt(2<=2),
+    testInt(2<=11),
+    testInt(20<=11),
+    testInt(2>2),
+    testInt(2>11),
+    testInt(20>11),
+    testInt(2>=2),
+    testInt(2>=11),
+    testInt(20>=11),
+    testInt(2==2),
+    testInt(2==11),
+    testInt(20==11),
+    testInt(2!=2),
+    testInt(2!=11),
+    testInt(20!=11),
+#ifdef SK_CAN_USE_FLOAT
+    // left int, right scalar
+    testInt(2<2.),
+    testInt(2<11.),
+    testInt(20<11.),
+    testInt(2<=2.),
+    testInt(2<=11.),
+    testInt(20<=11.),
+    testInt(2>2.),
+    testInt(2>11.),
+    testInt(20>11.),
+    testInt(2>=2.),
+    testInt(2>=11.),
+    testInt(20>=11.),
+    testInt(2==2.),
+    testInt(2==11.),
+    testInt(20==11.),
+    testInt(2!=2.),
+    testInt(2!=11.),
+    testInt(20!=11.),
+    // left scalar, right int
+    testInt(2.<2),
+    testInt(2.<11),
+    testInt(20.<11),
+    testInt(2.<=2),
+    testInt(2.<=11),
+    testInt(20.<=11),
+    testInt(2.>2),
+    testInt(2.>11),
+    testInt(20.>11),
+    testInt(2.>=2),
+    testInt(2.>=11),
+    testInt(20.>=11),
+    testInt(2.==2),
+    testInt(2.==11),
+    testInt(20.==11),
+    testInt(2.!=2),
+    testInt(2.!=11),
+    testInt(20.!=11),
+    // both scalar
+    testInt(2.<11.),
+    testInt(20.<11.),
+    testInt(2.<=2.),
+    testInt(2.<=11.),
+    testInt(20.<=11.),
+    testInt(2.>2.),
+    testInt(2.>11.),
+    testInt(20.>11.),
+    testInt(2.>=2.),
+    testInt(2.>=11.),
+    testInt(20.>=11.),
+    testInt(2.==2.),
+    testInt(2.==11.),
+    testInt(20.==11.),
+    testInt(2.!=2.),
+    testInt(2.!=11.),
+    testInt(20.!=11.),
+#endif
+    // int, string (string is int)
+    testFalse(2<'2'),
+    testTrue(2<'11'),
+    testFalse(20<'11'),
+    testTrue(2<='2'),
+    testTrue(2<='11'),
+    testFalse(20<='11'),
+    testFalse(2>'2'),
+    testFalse(2>'11'),
+    testTrue(20>'11'),
+    testTrue(2>='2'),
+    testFalse(2>='11'),
+    testTrue(20>='11'),
+    testTrue(2=='2'),
+    testFalse(2=='11'),
+    testFalse(2!='2'),
+    testTrue(2!='11'),
+    // int, string (string is scalar)
+    testFalse(2<'2.'),
+    testTrue(2<'11.'),
+    testFalse(20<'11.'),
+    testTrue(2=='2.'),
+    testFalse(2=='11.'),
+#ifdef SK_CAN_USE_FLOAT
+    // scalar, string
+    testFalse(2.<'2.'),
+    testTrue(2.<'11.'),
+    testFalse(20.<'11.'),
+    testTrue(2.=='2.'),
+    testFalse(2.=='11.'),
+    // string, int
+    testFalse('2'<2),
+    testTrue('2'<11),
+    testFalse('20'<11),
+    testTrue('2'==2),
+    testFalse('2'==11),
+    // string, scalar
+    testFalse('2'<2.),
+    testTrue('2'<11.),
+    testFalse('20'<11.),
+    testTrue('2'==2.),
+    testFalse('2'==11.),
+#endif
+    // string, string
+    testFalse('2'<'2'),
+    testFalse('2'<'11'),
+    testFalse('20'<'11'),
+    testTrue('2'=='2'),
+    testFalse('2'=='11'),
+    // logic
+    testInt(1?2:3),
+    testInt(0?2:3),
+    testInt((1&&2)||3),
+    testInt((1&&0)||3),
+    testInt((1&&0)||0),
+    testInt(1||(0&&3)),
+    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)
+
+void SkScriptEngine2::UnitTest() {
+#if defined(SK_SUPPORT_UNITTEST)
+    ValidateDecompileTable();
+    for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
+        SkScriptEngine2 engine(scriptTests[index].fType);
+        SkScriptValue2 value;
+        const char* script = scriptTests[index].fScript;
+        const char* scriptPtr = script;
+        SkASSERT(engine.evaluateScript(&scriptPtr, &value) == true);
+        SkASSERT(value.fType == scriptTests[index].fType);
+        SkScalar error;
+        switch (value.fType) {
+            case SkOperand2::kS32:
+                if (value.fOperand.fS32 != scriptTests[index].fIntAnswer)
+                    SkDEBUGF(("script '%s' == value %d != expected answer %d\n", script, value.fOperand.fS32, scriptTests[index].fIntAnswer));
+                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+                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:
+                SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+#endif
+}
+#endif
diff --git a/legacy/src/animator/SkSnapshot.cpp b/legacy/src/animator/SkSnapshot.cpp
new file mode 100644
index 0000000..4e78bbd
--- /dev/null
+++ b/legacy/src/animator/SkSnapshot.cpp
@@ -0,0 +1,64 @@
+
+/*
+ * 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 "SkTypes.h"
+
+#include "SkSnapshot.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkImageEncoder.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSnapshot::fInfo[] = {
+    SK_MEMBER(filename, String),
+    SK_MEMBER(quality, Float),
+    SK_MEMBER(sequence, Boolean),
+    SK_MEMBER(type, BitmapEncoding)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSnapshot);
+
+SkSnapshot::SkSnapshot()
+{
+    quality     = 100 * SK_Scalar1;
+    type        = (SkImageEncoder::Type) -1;
+    sequence    = false;
+    fSeqVal     = 0;
+}
+
+#include "SkDevice.h"
+
+bool SkSnapshot::draw(SkAnimateMaker& maker) {
+    SkASSERT(type >= 0);
+    SkASSERT(filename.size() > 0);
+    SkImageEncoder* encoder = SkImageEncoder::Create((SkImageEncoder::Type) type);
+
+    SkString name(filename);
+    if (sequence) {
+        char num[4] = "000";
+        num[0] = (char) (num[0] + fSeqVal / 100);
+        num[1] = (char) (num[1] + fSeqVal / 10 % 10);
+        num[2] = (char) (num[2] + fSeqVal % 10);
+        name.append(num);
+        if (++fSeqVal > 999)
+            sequence = false;
+    }
+    if (type == SkImageEncoder::kJPEG_Type)
+        name.append(".jpg");
+    else if (type == SkImageEncoder::kPNG_Type)
+        name.append(".png");
+    encoder->encodeFile(name.c_str(),
+                        maker.fCanvas->getDevice()->accessBitmap(false),
+                        SkScalarFloor(quality));
+    return false;
+}
+
diff --git a/legacy/src/animator/SkSnapshot.h b/legacy/src/animator/SkSnapshot.h
new file mode 100644
index 0000000..beb26ca
--- /dev/null
+++ b/legacy/src/animator/SkSnapshot.h
@@ -0,0 +1,31 @@
+
+/*
+ * 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 SkSnapShot_DEFINED
+#define SkSnapShot_DEFINED
+
+#include "SkDrawable.h"
+#include "SkImageDecoder.h"
+#include "SkMemberInfo.h"
+#include "SkString.h"
+
+class SkSnapshot: public SkDrawable {
+    DECLARE_MEMBER_INFO(Snapshot);
+    SkSnapshot();
+    virtual bool draw(SkAnimateMaker& );
+    private:
+    SkString filename;
+    SkScalar quality;
+    SkBool sequence;
+    int /*SkImageEncoder::Type*/    type;
+    int fSeqVal;
+};
+
+#endif // SkSnapShot_DEFINED
+
diff --git a/legacy/src/animator/SkTDArray_Experimental.h b/legacy/src/animator/SkTDArray_Experimental.h
new file mode 100644
index 0000000..e598a6d
--- /dev/null
+++ b/legacy/src/animator/SkTDArray_Experimental.h
@@ -0,0 +1,142 @@
+
+/*
+ * 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 SkTDArray_Experimental_DEFINED
+#define SkTDArray_Experimental_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_UNIX
+#define SK_BUILD_FOR_ADS_12
+#endif
+
+#ifndef SK_BUILD_FOR_ADS_12
+#define SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT 1
+#else
+#define SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT 0
+#endif
+
+#if SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT == 0
+#include "SkTDArray.h"
+#define SkIntArray(type) SkTDArray<type>
+#define SkLongArray(type) SkTDArray<type>
+#else
+
+class SkDS32Array {
+protected:
+    SkDS32Array();
+    SkDS32Array(const SkDS32Array& src);
+    SkDS32Array(const int32_t src[], U16CPU count);
+    SkDS32Array& operator=(const SkDS32Array& src);
+    friend int operator==(const SkDS32Array& a, const SkDS32Array& b);
+    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(); 
+        *result = 0;
+        return result;
+    }
+
+    int find(const int32_t& elem) const;
+    int32_t* insert(U16CPU index, U16CPU count, const int32_t* src);
+    int rfind(const int32_t& elem) const;
+    void swap(SkDS32Array& other);
+public:
+    bool isEmpty() const { return fCount == 0; }
+    int count() const { return fCount; }
+
+    void remove(U16CPU index, U16CPU count = 1)
+    {
+        SkASSERT(index + count <= fCount);
+        fCount = SkToU16(fCount - count);
+        memmove(fArray + index, fArray + index + count, sizeof(int32_t) * (fCount - index));
+    }
+
+    void reset()
+    {
+        if (fArray)
+        {
+            sk_free(fArray);
+            fArray = NULL;
+#ifdef SK_DEBUG
+            fData = NULL;
+#endif
+            fReserve = fCount = 0;
+        }
+        else
+        {
+            SkASSERT(fReserve == 0 && fCount == 0);
+        }
+    }
+
+    void setCount(U16CPU count)
+    {
+        if (count > fReserve)
+            this->growBy(count - fCount);
+        else
+            fCount = SkToU16(count);
+    }
+protected:
+#ifdef SK_DEBUG
+    enum {
+        kDebugArraySize = 24
+    };
+    int32_t(* fData)[kDebugArraySize];
+#endif
+    int32_t*    fArray;
+    uint16_t    fReserve, fCount;
+    void growBy(U16CPU extra);
+};
+
+#ifdef SK_DEBUG
+    #define SYNC() fTData = (T (*)[kDebugArraySize]) fArray
+#else
+    #define SYNC()
+#endif
+
+template <typename T> class SkTDS32Array : public SkDS32Array {
+public:
+    SkTDS32Array() { SkDEBUGCODE(fTData=NULL); SkASSERT(sizeof(T) == sizeof(int32_t)); }
+    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) { 
+        return (SkTDS32Array<T>&) SkDS32Array::operator=(src); }
+    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(); }
+    T* append(U16CPU count, const T* src = NULL) { return (T*) SkDS32Array::append(count, (const int32_t*) src); }
+    T*  begin() const { SYNC(); return (T*) fArray; }
+    T*  end() const { return (T*) (fArray ? fArray + fCount : NULL); }
+    int find(const T& elem) const { return SkDS32Array::find((const int32_t&) elem); }
+    T* insert(U16CPU index) { return this->insert(index, 1, NULL); }
+    T* insert(U16CPU index, U16CPU count, const T* src = NULL) {
+        return (T*) SkDS32Array::insert(index, count, (const int32_t*) src); }
+    int rfind(const T& elem) const { return SkDS32Array::rfind((const int32_t&) elem); }
+    T*          push() { return this->append(); }
+    void        push(T& elem) { *this->append() = elem; }
+    const T&    top() const { return (*this)[fCount - 1]; }
+    T&          top() { return (*this)[fCount - 1]; }
+    void        pop(T* elem) { if (elem) *elem = (*this)[fCount - 1]; --fCount; }
+    void        pop() { --fCount; }
+private:
+#ifdef SK_DEBUG
+    mutable T(* fTData)[kDebugArraySize];
+#endif
+};
+
+#define SkIntArray(type) SkTDS32Array<type> // holds 32 bit data types
+#define SkLongArray(type) SkTDS32Array<type>    // holds 32/64 bit data types depending on pointer size
+
+#endif // SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT
+
+#endif // SkTDArray_Experimental_DEFINED
diff --git a/legacy/src/animator/SkTextOnPath.cpp b/legacy/src/animator/SkTextOnPath.cpp
new file mode 100644
index 0000000..e7f7651
--- /dev/null
+++ b/legacy/src/animator/SkTextOnPath.cpp
@@ -0,0 +1,39 @@
+
+/*
+ * 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 "SkTextOnPath.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawPath.h"
+#include "SkDrawText.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkTextOnPath::fInfo[] = {
+    SK_MEMBER(offset, Float),
+    SK_MEMBER(path, Path),
+    SK_MEMBER(text, Text)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkTextOnPath);
+
+SkTextOnPath::SkTextOnPath() : offset(0), path(NULL), text(NULL) {
+}
+
+bool SkTextOnPath::draw(SkAnimateMaker& maker) {
+    SkASSERT(text);
+    SkASSERT(path);
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawTextOnPathHV(text->getText(), text->getSize(), 
+                                    path->getPath(), offset, 0, *maker.fPaint);
+    return false;
+}
diff --git a/legacy/src/animator/SkTextOnPath.h b/legacy/src/animator/SkTextOnPath.h
new file mode 100644
index 0000000..b0ce234
--- /dev/null
+++ b/legacy/src/animator/SkTextOnPath.h
@@ -0,0 +1,30 @@
+
+/*
+ * 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 SkTextOnPath_DEFINED
+#define SkTextOnPath_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+
+class SkDrawPath;
+class SkText;
+
+class SkTextOnPath : public SkBoundable {
+    DECLARE_MEMBER_INFO(TextOnPath);
+    SkTextOnPath();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkScalar offset;
+    SkDrawPath* path;
+    SkText* text;
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkTextOnPath_DEFINED
diff --git a/legacy/src/animator/SkTextToPath.cpp b/legacy/src/animator/SkTextToPath.cpp
new file mode 100644
index 0000000..cf78ff5
--- /dev/null
+++ b/legacy/src/animator/SkTextToPath.cpp
@@ -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.
+ */
+
+
+#include "SkTextToPath.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawPaint.h"
+#include "SkDrawPath.h"
+#include "SkDrawText.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkTextToPath::fInfo[] = {
+    SK_MEMBER(paint, Paint),
+    SK_MEMBER(path, Path),
+    SK_MEMBER(text, Text)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkTextToPath);
+
+SkTextToPath::SkTextToPath() : paint(NULL), path(NULL), text(NULL) {
+}
+
+bool SkTextToPath::draw(SkAnimateMaker& maker) {
+    path->draw(maker);
+    return false;
+}
+
+void SkTextToPath::onEndElement(SkAnimateMaker& maker) {
+    if (paint == NULL || path == NULL || text == NULL) {
+        // !!! add error message here
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorInAttributeValue);
+        return;
+    }
+    SkPaint realPaint;
+    paint->setupPaint(&realPaint);
+    realPaint.getTextPath(text->getText(), text->getSize(), text->x, 
+        text->y, &path->getPath());
+}
+
diff --git a/legacy/src/animator/SkTextToPath.h b/legacy/src/animator/SkTextToPath.h
new file mode 100644
index 0000000..cb2a15e
--- /dev/null
+++ b/legacy/src/animator/SkTextToPath.h
@@ -0,0 +1,32 @@
+
+/*
+ * 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 SkTextToPath_DEFINED
+#define SkTextToPath_DEFINED
+
+#include "SkDrawPath.h"
+#include "SkMemberInfo.h"
+
+class SkDrawPaint;
+class SkDrawPath;
+class SkText;
+
+class SkTextToPath : public SkDrawable {
+    DECLARE_MEMBER_INFO(TextToPath);
+    SkTextToPath();
+    virtual bool draw(SkAnimateMaker& );
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    SkDrawPaint* paint;
+    SkDrawPath* path;
+    SkText* text;
+};
+
+#endif // SkTextToPath_DEFINED
+
diff --git a/legacy/src/animator/SkTime.cpp b/legacy/src/animator/SkTime.cpp
new file mode 100644
index 0000000..35c7eb8
--- /dev/null
+++ b/legacy/src/animator/SkTime.cpp
@@ -0,0 +1,81 @@
+
+/*
+ * 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 "SkTime.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+#ifdef SK_DEBUG
+SkMSec gForceTickCount = (SkMSec) -1;
+#endif
+
+void SkTime::GetDateTime(DateTime* t)
+{
+    if (t)
+    {
+        SYSTEMTIME  syst;
+
+        ::GetLocalTime(&syst);
+        t->fYear        = SkToU16(syst.wYear);
+        t->fMonth       = SkToU8(syst.wMonth);
+        t->fDayOfWeek   = SkToU8(syst.wDayOfWeek);
+        t->fDay         = SkToU8(syst.wDay);
+        t->fHour        = SkToU8(syst.wHour);
+        t->fMinute      = SkToU8(syst.wMinute);
+        t->fSecond      = SkToU8(syst.wSecond);
+    }
+}
+
+SkMSec SkTime::GetMSecs()
+{
+#ifdef SK_DEBUG
+    if (gForceTickCount != (SkMSec) -1)
+        return gForceTickCount;
+#endif
+    return ::GetTickCount();
+}
+
+#elif defined(xSK_BUILD_FOR_MAC)
+
+#include <time.h>
+
+void SkTime::GetDateTime(DateTime* t)
+{
+    if (t)
+    {
+        tm      syst;
+        time_t  tm;
+        
+        time(&tm);
+        localtime_r(&tm, &syst);
+        t->fYear        = SkToU16(syst.tm_year);
+        t->fMonth       = SkToU8(syst.tm_mon + 1);
+        t->fDayOfWeek   = SkToU8(syst.tm_wday);
+        t->fDay         = SkToU8(syst.tm_mday);
+        t->fHour        = SkToU8(syst.tm_hour);
+        t->fMinute      = SkToU8(syst.tm_min);
+        t->fSecond      = SkToU8(syst.tm_sec);
+    }
+}
+
+#include "Sk64.h"
+
+SkMSec SkTime::GetMSecs()
+{
+    UnsignedWide    wide;
+    Sk64            s;
+
+    ::Microseconds(&wide);
+    s.set(wide.hi, wide.lo);
+    s.div(1000, Sk64::kRound_DivOption);
+    return s.get32();
+}
+
+#endif
+
diff --git a/legacy/src/animator/SkTypedArray.cpp b/legacy/src/animator/SkTypedArray.cpp
new file mode 100644
index 0000000..e94e57d
--- /dev/null
+++ b/legacy/src/animator/SkTypedArray.cpp
@@ -0,0 +1,179 @@
+
+/*
+ * 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 "SkTypedArray.h"
+
+SkTypedArray::SkTypedArray() : fType(SkType_Unknown) {
+}
+
+SkTypedArray::SkTypedArray(SkDisplayTypes type) : fType(type) {
+}
+
+bool SkTypedArray::getIndex(int index, SkOperand* operand) {
+    if (index >= count()) {
+        SkASSERT(0);
+        return false;
+    }
+    *operand = begin()[index];
+    return true;
+}
+
+
+#if SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT == 1
+SkDS32Array::SkDS32Array()
+{
+    fReserve = fCount = 0;
+    fArray = NULL;
+#ifdef SK_DEBUG
+    fData = NULL;
+#endif
+}
+
+SkDS32Array::SkDS32Array(const SkDS32Array& src)
+{
+    fReserve = fCount = 0;
+    fArray = NULL;
+#ifdef SK_DEBUG
+    fData = NULL;
+#endif
+    SkDS32Array tmp(src.fArray, src.fCount);
+    this->swap(tmp);
+}
+
+SkDS32Array::SkDS32Array(const int32_t src[], U16CPU count)
+{
+    SkASSERT(src || count == 0);
+
+    fReserve = fCount = 0;
+    fArray = NULL;
+#ifdef SK_DEBUG
+    fData = NULL;
+#endif
+    if (count)
+    {
+        fArray = (int32_t*)sk_malloc_throw(count * sizeof(int32_t));
+#ifdef SK_DEBUG
+        fData = (int32_t (*)[kDebugArraySize]) fArray;
+#endif
+        memcpy(fArray, src, sizeof(int32_t) * count);
+        fReserve = fCount = SkToU16(count);
+    }
+}
+
+SkDS32Array& SkDS32Array::operator=(const SkDS32Array& src)
+{
+    if (this != &src)
+    {
+        if (src.fCount > fReserve)
+        {
+            SkDS32Array tmp(src.fArray, src.fCount);
+            this->swap(tmp);
+        }
+        else
+        {
+            memcpy(fArray, src.fArray, sizeof(int32_t) * src.fCount);
+            fCount = src.fCount;
+        }
+    }
+    return *this;
+}
+
+int operator==(const SkDS32Array& a, const SkDS32Array& b)
+{
+    return a.fCount == b.fCount &&
+            (a.fCount == 0 || !memcmp(a.fArray, b.fArray, a.fCount * sizeof(int32_t)));
+}
+
+void SkDS32Array::swap(SkDS32Array& other)
+{
+    SkTSwap(fArray, other.fArray);
+#ifdef SK_DEBUG
+    SkTSwap(fData, other.fData);
+#endif
+    SkTSwap(fReserve, other.fReserve);
+    SkTSwap(fCount, other.fCount);
+}
+
+int32_t* SkDS32Array::append(U16CPU count, const int32_t* src)
+{
+    unsigned oldCount = fCount;
+    if (count)
+    {
+        SkASSERT(src == NULL || fArray == NULL ||
+                src + count <= fArray || fArray + count <= src);
+
+        this->growBy(count);
+        if (src)
+            memcpy(fArray + oldCount, src, sizeof(int32_t) * count);
+    }
+    return fArray + oldCount;
+}
+
+int SkDS32Array::find(const int32_t& elem) const
+{
+    const int32_t* iter = fArray;
+    const int32_t* stop = fArray + fCount;
+
+    for (; iter < stop; iter++)
+    {
+        if (*iter == elem)
+            return (int) (iter - fArray);
+    }
+    return -1;
+}
+
+void SkDS32Array::growBy(U16CPU extra)
+{
+    SkASSERT(extra);
+    SkASSERT(fCount + extra <= 0xFFFF);
+
+    if (fCount + extra > fReserve)
+    {
+        size_t size = fCount + extra + 4;
+        size += size >> 2;
+        int32_t* array = (int32_t*)sk_malloc_throw(size * sizeof(int32_t));
+        memcpy(array, fArray, fCount * sizeof(int32_t));
+
+        sk_free(fArray);
+        fArray = array;
+#ifdef SK_DEBUG
+        fData = (int32_t (*)[kDebugArraySize]) fArray;
+#endif
+        fReserve = SkToU16((U16CPU)size);
+    }
+    fCount = SkToU16(fCount + extra);
+}
+
+int32_t* SkDS32Array::insert(U16CPU index, U16CPU count, const int32_t* src)
+{
+    SkASSERT(count);
+    int oldCount = fCount;
+    this->growBy(count);
+    int32_t* dst = fArray + index;
+    memmove(dst + count, dst, sizeof(int32_t) * (oldCount - index));
+    if (src)
+        memcpy(dst, src, sizeof(int32_t) * count);
+    return dst;
+}
+
+
+    int SkDS32Array::rfind(const int32_t& elem) const
+    {
+        const int32_t* iter = fArray + fCount;
+        const int32_t* stop = fArray;
+
+        while (iter > stop)
+        {
+            if (*--iter == elem)
+                return (int) (iter - stop);
+        }
+        return -1;
+    }
+
+#endif
diff --git a/legacy/src/animator/SkTypedArray.h b/legacy/src/animator/SkTypedArray.h
new file mode 100644
index 0000000..222c8df
--- /dev/null
+++ b/legacy/src/animator/SkTypedArray.h
@@ -0,0 +1,31 @@
+
+/*
+ * 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 SkTypedArray_DEFINED
+#define SkTypedArray_DEFINED
+
+#include "SkScript.h"
+#include "SkTDArray_Experimental.h"
+
+class SkTypedArray : public SkTDOperandArray {
+public:
+    SkTypedArray();
+    SkTypedArray(SkDisplayTypes type);
+    bool getIndex(int index, SkOperand* operand);
+    SkDisplayTypes getType() { return fType; }
+    SkScriptEngine::SkOpType getOpType() { return SkScriptEngine::ToOpType(fType); }
+    void setType(SkDisplayTypes type) { 
+    //  SkASSERT(count() == 0);
+        fType = type;
+    }
+protected:
+    SkDisplayTypes fType;
+};
+
+#endif // SkTypedArray_DEFINED
diff --git a/legacy/src/animator/SkXMLAnimatorWriter.cpp b/legacy/src/animator/SkXMLAnimatorWriter.cpp
new file mode 100644
index 0000000..6d299b6
--- /dev/null
+++ b/legacy/src/animator/SkXMLAnimatorWriter.cpp
@@ -0,0 +1,83 @@
+
+/*
+ * 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 "SkXMLAnimatorWriter.h"
+#include "SkAnimator.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayXMLParser.h"
+
+SkXMLAnimatorWriter::SkXMLAnimatorWriter(SkAnimator* animator) : fAnimator(animator)
+{
+    fParser = new SkDisplayXMLParser(*fAnimator->fMaker);
+}
+
+SkXMLAnimatorWriter::~SkXMLAnimatorWriter() {
+    delete fParser;
+}
+
+void SkXMLAnimatorWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    fParser->onAddAttributeLen(name, value, length);
+}
+
+void SkXMLAnimatorWriter::onEndElement()
+{
+    Elem* elem = getEnd();
+    fParser->onEndElement(elem->fName.c_str());
+    doEnd(elem);
+}
+
+void SkXMLAnimatorWriter::onStartElementLen(const char name[], size_t length)
+{
+    doStart(name, length);
+    fParser->onStartElementLen(name, length);
+}
+
+void SkXMLAnimatorWriter::writeHeader()
+{
+}
+
+#ifdef SK_DEBUG
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+void SkXMLAnimatorWriter::UnitTest(SkCanvas* canvas)
+{
+    SkAnimator  s;
+    SkXMLAnimatorWriter     w(&s);
+    w.startElement("screenplay");
+        w.startElement("animateField");
+            w.addAttribute("field", "x1");
+            w.addAttribute("id", "to100");
+            w.addAttribute("from", "0");
+            w.addAttribute("to", "100");
+            w.addAttribute("dur", "1");
+        w.endElement();
+        w.startElement("event");
+            w.addAttribute("kind", "onLoad");
+            w.startElement("line");
+                w.addAttribute("id", "line");
+                w.addAttribute("x1", "-1");
+                w.addAttribute("y1", "20");
+                w.addAttribute("x2", "150");
+                w.addAttribute("y2", "40");
+            w.endElement();
+            w.startElement("apply");
+                w.addAttribute("animator", "to100");
+                w.addAttribute("scope", "line");
+            w.endElement();
+        w.endElement();
+    w.endElement();
+    SkPaint paint;
+    canvas->drawColor(SK_ColorWHITE);
+    s.draw(canvas, &paint, 0);
+}
+
+#endif
+
diff --git a/legacy/src/animator/SkXMLAnimatorWriter.h b/legacy/src/animator/SkXMLAnimatorWriter.h
new file mode 100644
index 0000000..bd6ccf4
--- /dev/null
+++ b/legacy/src/animator/SkXMLAnimatorWriter.h
@@ -0,0 +1,34 @@
+
+/*
+ * 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 SkXMLAnimatorWriter_DEFINED
+#define SkXMLAnimatorWriter_DEFINED
+
+#include "SkXMLWriter.h"
+
+class SkAnimator;
+class SkDisplayXMLParser;
+
+class SkXMLAnimatorWriter : public SkXMLWriter {
+public:
+    SkXMLAnimatorWriter(SkAnimator*);
+    virtual ~SkXMLAnimatorWriter();
+    virtual void    writeHeader();
+    SkDEBUGCODE(static void UnitTest(class SkCanvas* canvas);)
+protected:
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length);
+    virtual void onEndElement();
+    virtual void onStartElementLen(const char elem[], size_t length);
+private:
+    SkAnimator* fAnimator;
+    SkDisplayXMLParser* fParser;
+};
+
+#endif // SkXMLAnimatorWriter_DEFINED
+
diff --git a/legacy/src/animator/thingstodo.txt b/legacy/src/animator/thingstodo.txt
new file mode 100644
index 0000000..8d0d47a
--- /dev/null
+++ b/legacy/src/animator/thingstodo.txt
@@ -0,0 +1,21 @@
+things to do:
+	figure out where endless or very deep recursion is possible
+	at these points, generate an error if actual physical stack gets too large
+	candidates are scripts
+		eval(eval(eval...	user callouts
+		(((((	operator precedence or similar making stack deep
+		groups within groups
+		very large apply create or apply immediate steps
+
+	write tests for math functions
+		looks like random takes a parameter when it should take zero parameters
+		
+	add Math, Number files to perforce for docs
+	alphabetize attributes in docs
+	
+	manually modified tools/screenplayDocs/xmlToJPEG.cpp
+	
+	fix docs where lines are stitched together (insert space)
+	
+	naked <data> outside of <post> asserts on name
+	handle errors for all element not contained by correct parents
\ No newline at end of file
diff --git a/legacy/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h b/legacy/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
new file mode 100644
index 0000000..e76ab08
--- /dev/null
+++ b/legacy/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
@@ -0,0 +1,178 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+class ARGB32_Clamp_Bilinear_BitmapShader : public SkBitmapShader {
+public:
+    ARGB32_Clamp_Bilinear_BitmapShader(const SkBitmap& src)
+        : SkBitmapShader(src, true,
+                         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)
+    {}
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+};
+
+SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY,
+                        const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table);
+SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY,
+                        const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table)
+{
+    int ix = fx >> 16;
+    int iy = fy >> 16;
+
+    const SkPMColor *p00, *p01, *p10, *p11;
+
+    p00 = p01 = ((const SkPMColor*)((const char*)srcPixels
+                                    + SkClampMax(iy, srcMaxY) * srcRB))
+                                    + SkClampMax(ix, srcMaxX);
+
+    if ((unsigned)ix < srcMaxX)
+        p01 += 1;
+    p10 = p00;
+    p11 = p01;
+    if ((unsigned)iy < srcMaxY)
+    {
+        p10 = (const SkPMColor*)((const char*)p10 + srcRB);
+        p11 = (const SkPMColor*)((const char*)p11 + srcRB);
+    }
+
+    SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(proc_table, fx, fy);
+    return proc(p00, p01, p10, p11);
+}
+
+static inline SkPMColor sample_bilerpx(SkFixed fx, unsigned srcMaxX, const SkPMColor* srcPixels,
+                                       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);
+}
+
+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();
+    const SkBitmap& srcBitmap = this->getSrcBitmap();
+    unsigned        srcMaxX = srcBitmap.width() - 1;
+    unsigned        srcMaxY = srcBitmap.height() - 1;
+    unsigned        srcRB = srcBitmap.rowBytes();
+
+    const SkFilterPtrProc* proc_table = SkGetBilinearFilterPtrProcTable();
+    const SkPMColor* srcPixels = (const SkPMColor*)srcBitmap.getPixels();
+
+    if (this->getInverseClass() == kPerspective_MatrixClass)
+    {
+        SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                SkIntToScalar(y) + SK_ScalarHalf, count);
+
+        if (256 == srcScale)
+        {
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    SkFixed fy = *srcXY++ - SK_FixedHalf;
+                    *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                }
+            }
+        }
+        else    // scale by srcScale
+        {
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    SkFixed fy = *srcXY++ - SK_FixedHalf;
+                    SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                    *dstC++ = SkAlphaMulQ(c, srcScale);
+                }
+            }
+        }
+    }
+    else    // linear case
+    {
+        SkFixed fx, fy, dx, dy;
+
+        // now init fx, fy, dx, dy
+        {
+            SkPoint srcPt;
+            this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                             SkIntToScalar(y) + SK_ScalarHalf,
+                                             &srcPt);
+
+            fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+            fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+            if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+            else
+            {
+                dx = SkScalarToFixed(inv.getScaleX());
+                dy = SkScalarToFixed(inv.getSkewY());
+            }
+        }
+
+        if (dy == 0 && (unsigned)(fy >> 16) < srcMaxY)
+        {
+            srcPixels = (const SkPMColor*)((const char*)srcPixels + (fy >> 16) * srcRB);
+            proc_table = SkGetBilinearFilterPtrProcYTable(proc_table, fy);
+            if (256 == srcScale)
+            {
+                do {
+                    *dstC++ = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table);
+                    fx += dx;
+                } while (--count != 0);
+            }
+            else
+            {
+                do {
+                    SkPMColor c = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table);
+                    *dstC++ = SkAlphaMulQ(c, srcScale);
+                    fx += dx;
+                } while (--count != 0);
+            }
+        }
+        else    // dy is != 0
+        {
+            if (256 == srcScale)
+            {
+                do {
+                    *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                    fx += dx;
+                    fy += dy;
+                } while (--count != 0);
+            }
+            else
+            {
+                do {
+                    SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                    *dstC++ = SkAlphaMulQ(c, srcScale);
+                    fx += dx;
+                    fy += dy;
+                } while (--count != 0);
+            }
+        }
+    }
+}
+
diff --git a/legacy/src/core/Sk64.cpp b/legacy/src/core/Sk64.cpp
new file mode 100644
index 0000000..0d74904
--- /dev/null
+++ b/legacy/src/core/Sk64.cpp
@@ -0,0 +1,363 @@
+
+/*
+ * 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 "Sk64.h"
+#include "SkMath.h"
+
+#define shift_left(hi, lo)          \
+    hi = (hi << 1) | (lo >> 31);    \
+    lo <<= 1
+
+#define shift_left_bits(hi, lo, bits)           \
+    SkASSERT((unsigned)(bits) < 31);                \
+    hi = (hi << (bits)) | (lo >> (32 - (bits)));    \
+    lo <<= (bits)
+
+//////////////////////////////////////////////////////////////////////
+
+int Sk64::getClzAbs() const
+{
+    int32_t     hi = fHi;
+    uint32_t    lo = fLo;
+
+    // get abs
+    if (hi < 0)
+    {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+    return hi ? SkCLZ(hi) : SkCLZ(lo) + 32;
+}
+
+void Sk64::shiftLeft(unsigned bits)
+{
+    SkASSERT(bits <= 63);
+    if (bits == 0)
+        return;
+
+    if (bits >= 32)
+    {
+        fHi = fLo << (bits - 32);
+        fLo = 0;
+    }
+    else
+    {
+        fHi = (fHi << bits) | (fLo >> (32 - bits));
+        fLo <<= bits;
+    }
+}
+
+int32_t Sk64::getShiftRight(unsigned bits) const
+{
+    SkASSERT(bits <= 63);
+
+    if (bits == 0)
+        return fLo;
+
+    if (bits >= 32)
+        return fHi >> (bits - 32);
+    else
+    {
+#ifdef SK_DEBUG
+        int32_t tmp = fHi >> bits;
+        SkASSERT(tmp == 0 || tmp == -1);
+#endif
+        return (fHi << (32 - bits)) | (fLo >> bits);
+    }
+}
+
+void Sk64::shiftRight(unsigned bits)
+{
+    SkASSERT(bits <= 63);
+    if (bits == 0)
+        return;
+
+    if (bits >= 32)
+    {
+        fLo = fHi >> (bits - 32);
+        fHi >>= 31;
+    }
+    else
+    {
+        fLo = (fHi << (32 - bits)) | (fLo >> bits);
+        fHi >>= bits;
+    }
+}
+
+void Sk64::roundRight(unsigned bits)
+{
+    SkASSERT(bits <= 63);
+    if (bits)
+    {
+        Sk64 one;
+        one.set(1);
+        one.shiftLeft(bits - 1);
+        this->add(one);
+        this->shiftRight(bits);
+    }
+}
+
+int Sk64::shiftToMake32() const
+{
+    int32_t     hi = fHi;
+    uint32_t    lo = fLo;
+
+    if (hi < 0) // make it positive
+    {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+
+    if (hi == 0)
+        return lo >> 31;
+    else
+        return 33 - SkCLZ(hi);
+}
+
+void Sk64::negate()
+{
+    fHi = -fHi - Sk32ToBool(fLo);
+    fLo = 0 - fLo;
+}
+
+void Sk64::abs()
+{
+    if (fHi < 0)
+    {
+        fHi = -fHi - Sk32ToBool(fLo);
+        fLo = 0 - fLo;
+    }
+}
+
+////////////////////////////////////////////////////////////////
+
+static inline int32_t round_right_16(int32_t hi, uint32_t lo)
+{
+    uint32_t sum = lo + (1 << 15);
+    hi += (sum < lo);
+    return (hi << 16) | (sum >> 16);
+}
+
+SkBool Sk64::isFixed() const
+{
+    Sk64 tmp = *this;
+    tmp.roundRight(16);
+    return tmp.is32();
+}
+
+SkFract Sk64::getFract() const
+{
+    Sk64 tmp = *this;
+    tmp.roundRight(30);
+    return tmp.get32();
+}
+
+void Sk64::sub(const Sk64& a)
+{
+    fHi = fHi - a.fHi - (fLo < a.fLo);
+    fLo = fLo - a.fLo;
+}
+
+void Sk64::rsub(const Sk64& a)
+{
+    fHi = a.fHi - fHi - (a.fLo < fLo);
+    fLo = a.fLo - fLo;
+}
+
+void Sk64::setMul(int32_t a, int32_t b)
+{
+    int sa = a >> 31;
+    int sb = b >> 31;
+    // now make them positive
+    a = (a ^ sa) - sa;
+    b = (b ^ sb) - sb;
+
+    uint32_t    ah = a >> 16;
+    uint32_t    al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t A = ah * bh;
+    uint32_t B = ah * bl + al * bh;
+    uint32_t C = al * bl;
+
+    /*  [  A  ]
+           [  B  ]
+              [  C  ]
+    */
+    fLo = C + (B << 16);
+    fHi = A + (B >>16) + (fLo < C);
+
+    if (sa != sb)
+        this->negate();
+}
+
+void Sk64::div(int32_t denom, DivOptions option)
+{
+    SkASSERT(denom);
+
+    int32_t     hi = fHi;
+    uint32_t    lo = fLo;
+    int         sign = denom ^ hi;
+
+    denom = SkAbs32(denom);
+    if (hi < 0)
+    {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+
+    if (option == kRound_DivOption) // add denom/2
+    {
+        uint32_t newLo = lo + (denom >> 1);
+        hi += (newLo < lo);
+        lo = newLo;
+    }
+
+    if (hi == 0)    // fast-case
+    {
+        if (lo < (uint32_t)denom)
+            this->set(0, 0);
+        else
+        {
+            this->set(0, lo / denom);
+            if (sign < 0)
+                this->negate();
+        }
+        return;
+    }
+
+    int bits;
+
+    {
+        int dbits = SkCLZ(denom);
+        int nbits = SkCLZ(hi);
+
+        bits = 32 + dbits - nbits;
+        SkASSERT(bits <= 63);
+        if (bits <= 0)
+        {
+            this->set(0, 0);
+            return;
+        }
+        denom <<= (dbits - 1);
+        shift_left_bits(hi, lo, nbits - 1);
+    }
+
+    int32_t     rhi = 0;
+    uint32_t    rlo = 0;
+
+    do {
+        shift_left(rhi, rlo);
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+        if ((uint32_t)denom <= (uint32_t)hi)
+        {
+            hi -= denom;
+            rlo |= 1;
+        }
+#else
+        int32_t diff = (denom - hi - 1) >> 31;
+        hi -= denom & diff;
+        rlo -= diff;
+#endif
+        shift_left(hi, lo);
+    } while (--bits >= 0);
+    SkASSERT(rhi >= 0);
+
+    fHi = rhi;
+    fLo = rlo;
+    if (sign < 0)
+        this->negate();
+}
+
+#define shift_left_2(a, b, c)   \
+    a = (a << 2) | (b >> 30);   \
+    b = (b << 2) | (c >> 30);   \
+    c <<= 2
+
+int32_t Sk64::getSqrt() const
+{
+    SkASSERT(!this->isNeg());
+
+    uint32_t    hi = fHi;
+    uint32_t lo = fLo;
+    uint32_t    sqr = 0;
+    uint32_t root = 0;
+    int count = 31;
+
+    do {
+        root <<= 1;
+        shift_left_2(sqr, hi, lo);
+
+        uint32_t testDiv = (root << 1) + 1;
+        if (sqr >= testDiv)
+        {
+            sqr -= testDiv;
+            root++;
+        }
+    } while (--count >= 0);
+    SkASSERT((int32_t)root >= 0);
+
+    return root;
+}
+
+#ifdef SkLONGLONG
+    SkLONGLONG Sk64::getLongLong() const
+    {
+        SkLONGLONG value = fHi;
+        value <<= 32;
+        return value | fLo;
+    }
+#endif
+
+SkFixed Sk64::getFixedDiv(const Sk64& denom) const
+{
+    Sk64    N = *this;
+    Sk64    D = denom;
+    int32_t sign = SkExtractSign(N.fHi ^ D.fHi);
+    SkFixed result;
+
+    N.abs();
+    D.abs();
+
+    // need to knock D down to just 31 bits
+    // either by rounding it to the right, or shifting N to the left
+    // then we can just call 64/32 div
+
+    int nclz = N.fHi ? SkCLZ(N.fHi) : 32;
+    int dclz = D.fHi ? SkCLZ(D.fHi) : (33 - (D.fLo >> 31));
+
+    int shiftN = nclz - 1;
+    SkASSERT(shiftN >= 0);
+    int shiftD = 33 - dclz;
+    SkASSERT(shiftD >= 0);
+
+    if (shiftD + shiftN < 16)
+        shiftD = 16 - shiftN;
+    else
+        shiftN = 16 - shiftD;
+
+    D.roundRight(shiftD);
+    if (D.isZero())
+        result = SK_MaxS32;
+    else
+    {
+        if (shiftN >= 0)
+            N.shiftLeft(shiftN);
+        else
+            N.roundRight(-shiftN);
+        N.div(D.get32(), Sk64::kTrunc_DivOption);
+        if (N.is32())
+            result = N.get32();
+        else
+            result = SK_MaxS32;
+    }
+    return SkApplySign(result, sign);
+}
+
diff --git a/legacy/src/core/SkAAClip.cpp b/legacy/src/core/SkAAClip.cpp
new file mode 100644
index 0000000..64c2728
--- /dev/null
+++ b/legacy/src/core/SkAAClip.cpp
@@ -0,0 +1,2162 @@
+
+/*
+ * 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 "SkBlitter.h"
+#include "SkColorPriv.h"
+#include "SkPath.h"
+#include "SkScan.h"
+#include "SkThread.h"
+#include "SkUtils.h"
+
+class AutoAAClipValidate {
+public:
+    AutoAAClipValidate(const SkAAClip& clip) : fClip(clip) {
+        fClip.validate();
+    }
+    ~AutoAAClipValidate() {
+        fClip.validate();
+    }
+private:
+    const SkAAClip& fClip;
+};
+
+#ifdef SK_DEBUG
+    #define AUTO_AACLIP_VALIDATE(clip)  AutoAAClipValidate acv(clip)
+#else
+    #define AUTO_AACLIP_VALIDATE(clip)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define kMaxInt32   0x7FFFFFFF
+
+static inline bool x_in_rect(int x, const SkIRect& rect) {
+    return (unsigned)(x - rect.fLeft) < (unsigned)rect.width();
+}
+
+static inline bool y_in_rect(int y, const SkIRect& rect) {
+    return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
+}
+
+/*
+ *  Data runs are packed [count, alpha]
+ */
+
+struct SkAAClip::YOffset {
+    int32_t  fY;
+    uint32_t fOffset;
+};
+
+struct SkAAClip::RunHead {
+    int32_t fRefCnt;
+    int32_t fRowCount;
+    int32_t fDataSize;
+    
+    YOffset* yoffsets() {
+        return (YOffset*)((char*)this + sizeof(RunHead));
+    }
+    const YOffset* yoffsets() const {
+        return (const YOffset*)((const char*)this + sizeof(RunHead));
+    }
+    uint8_t* data() {
+        return (uint8_t*)(this->yoffsets() + fRowCount);
+    }
+    const uint8_t* data() const {
+        return (const uint8_t*)(this->yoffsets() + fRowCount);
+    }
+
+    static RunHead* Alloc(int rowCount, size_t dataSize) {
+        size_t size = sizeof(RunHead) + rowCount * sizeof(YOffset) + dataSize;
+        RunHead* head = (RunHead*)sk_malloc_throw(size);
+        head->fRefCnt = 1;
+        head->fRowCount = rowCount;
+        head->fDataSize = dataSize;
+        return head;
+    }
+
+    static int ComputeRowSizeForWidth(int width) {
+        // 2 bytes per segment, where each segment can store up to 255 for count
+        int segments = 0;
+        while (width > 0) {
+            segments += 1;
+            int n = SkMin32(width, 255);
+            width -= n;
+        }
+        return segments * 2;    // each segment is row[0] + row[1] (n + alpha)
+    }
+
+    static RunHead* AllocRect(const SkIRect& bounds) {
+        SkASSERT(!bounds.isEmpty());
+        int width = bounds.width();
+        size_t rowSize = ComputeRowSizeForWidth(width);
+        RunHead* head = RunHead::Alloc(1, rowSize);
+        YOffset* yoff = head->yoffsets();
+        yoff->fY = bounds.height() - 1;
+        yoff->fOffset = 0;
+        uint8_t* row = head->data();
+        while (width > 0) {
+            int n = SkMin32(width, 255);
+            row[0] = n;
+            row[1] = 0xFF;
+            width -= n;
+            row += 2;
+        }
+        return head;
+    }
+};
+
+class SkAAClip::Iter {
+public:
+    Iter(const SkAAClip&);
+
+    bool done() const { return fDone; }
+    int top() const { return fTop; }
+    int bottom() const { return fBottom; }
+    const uint8_t* data() const { return fData; }
+    void next();
+
+private:
+    const YOffset* fCurrYOff;
+    const YOffset* fStopYOff;
+    const uint8_t* fData;
+
+    int fTop, fBottom;
+    bool fDone;
+};
+
+SkAAClip::Iter::Iter(const SkAAClip& clip) {
+    if (clip.isEmpty()) {
+        fDone = true;
+        fTop = fBottom = clip.fBounds.fBottom;
+        fData = NULL;
+        return;
+    }
+    
+    const RunHead* head = clip.fRunHead;
+    fCurrYOff = head->yoffsets();
+    fStopYOff = fCurrYOff + head->fRowCount;
+    fData     = head->data() + fCurrYOff->fOffset;
+
+    // setup first value
+    fTop = clip.fBounds.fTop;
+    fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
+    fDone = false;
+}
+
+void SkAAClip::Iter::next() {
+    if (!fDone) {
+        const YOffset* prev = fCurrYOff;
+        const YOffset* curr = prev + 1;
+        SkASSERT(curr <= fStopYOff);
+
+        fTop = fBottom;
+        if (curr >= fStopYOff) {
+            fDone = true;
+            fBottom = kMaxInt32;
+            fData = NULL;
+        } else {
+            fBottom += curr->fY - prev->fY;
+            fData += curr->fOffset - prev->fOffset;
+            fCurrYOff = curr;
+        }
+    }
+}
+
+#ifdef SK_DEBUG
+// assert we're exactly width-wide, and then return the number of bytes used
+static size_t compute_row_length(const uint8_t row[], int width) {
+    const uint8_t* origRow = row;
+    while (width > 0) {
+        int n = row[0];
+        SkASSERT(n > 0);
+        SkASSERT(n <= width);
+        row += 2;
+        width -= n;
+    }
+    SkASSERT(0 == width);
+    return row - origRow;
+}
+
+void SkAAClip::validate() const {
+    if (NULL == fRunHead) {
+        SkASSERT(fBounds.isEmpty());
+        return;
+    }
+
+    const RunHead* head = fRunHead;
+    SkASSERT(head->fRefCnt > 0);
+    SkASSERT(head->fRowCount > 0);
+    SkASSERT(head->fDataSize > 0);
+
+    const YOffset* yoff = head->yoffsets();
+    const YOffset* ystop = yoff + head->fRowCount;
+    const int lastY = fBounds.height() - 1;
+
+    // Y and offset must be monotonic
+    int prevY = -1;
+    int32_t prevOffset = -1;
+    while (yoff < ystop) {
+        SkASSERT(prevY < yoff->fY);
+        SkASSERT(yoff->fY <= lastY);
+        prevY = yoff->fY;
+        SkASSERT(prevOffset < (int32_t)yoff->fOffset);
+        prevOffset = yoff->fOffset;
+        const uint8_t* row = head->data() + yoff->fOffset;
+        size_t rowLength = compute_row_length(row, fBounds.width());
+        SkASSERT(yoff->fOffset + rowLength <= (size_t) head->fDataSize);
+        yoff += 1;
+    }
+    // check the last entry;
+    --yoff;
+    SkASSERT(yoff->fY == lastY);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void count_left_right_zeros(const uint8_t* row, int width,
+                                   int* leftZ, int* riteZ) {
+    int zeros = 0;
+    do {
+        if (row[1]) {
+            break;
+        }
+        int n = row[0];
+        SkASSERT(n > 0);
+        SkASSERT(n <= width);
+        zeros += n;
+        row += 2;
+        width -= n;
+    } while (width > 0);
+    *leftZ = zeros;
+
+    zeros = 0;
+    while (width > 0) {
+        int n = row[0];
+        SkASSERT(n > 0);
+        if (0 == row[1]) {
+            zeros += n;
+        } else {
+            zeros = 0;
+        }
+        row += 2;
+        width -= n;
+    }
+    *riteZ = zeros;
+}
+
+#ifdef SK_DEBUG
+static void test_count_left_right_zeros() {
+    static bool gOnce;
+    if (gOnce) {
+        return;
+    }
+    gOnce = true;
+
+    const uint8_t data0[] = {  0, 0,     10, 0xFF };
+    const uint8_t data1[] = {  0, 0,     5, 0xFF, 2, 0, 3, 0xFF };
+    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 data6[] = {  2, 2,     2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
+
+    const uint8_t* array[] = {
+        data0, data1, data2, data3, data4, data5, data6
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
+        const uint8_t* data = array[i];
+        const int expectedL = *data++;
+        const int expectedR = *data++;
+        int L = 12345, R = 12345;
+        count_left_right_zeros(data, 10, &L, &R);
+        SkASSERT(expectedL == L);
+        SkASSERT(expectedR == R);
+    }
+}
+#endif
+
+// modify row in place, trimming off (zeros) from the left and right sides.
+// return the number of bytes that were completely eliminated from the left
+static int trim_row_left_right(uint8_t* row, int width, int leftZ, int riteZ) {
+    int trim = 0;
+    while (leftZ > 0) {
+        SkASSERT(0 == row[1]);
+        int n = row[0];
+        SkASSERT(n > 0);
+        SkASSERT(n <= width);
+        width -= n;
+        row += 2;
+        if (n > leftZ) {
+            row[-2] = n - leftZ;
+            break;
+        }
+        trim += 2;
+        leftZ -= n;
+        SkASSERT(leftZ >= 0);
+    }
+
+    if (riteZ) {
+        // walk row to the end, and then we'll back up to trim riteZ
+        while (width > 0) {
+            int n = row[0];
+            SkASSERT(n <= width);
+            width -= n;
+            row += 2;
+        }
+        // now skip whole runs of zeros
+        do {
+            row -= 2;
+            SkASSERT(0 == row[1]);
+            int n = row[0];
+            SkASSERT(n > 0);
+            if (n > riteZ) {
+                row[0] = n - riteZ;
+                break;
+            }
+            riteZ -= n;
+            SkASSERT(riteZ >= 0);
+        } while (riteZ > 0);
+    }
+    
+    return trim;
+}
+
+#ifdef SK_DEBUG
+// assert that this row is exactly this width
+static void assert_row_width(const uint8_t* row, int width) {
+    while (width > 0) {
+        int n = row[0];
+        SkASSERT(n > 0);
+        SkASSERT(n <= width);
+        width -= n;
+        row += 2;
+    }
+    SkASSERT(0 == width);
+}
+
+static void test_trim_row_left_right() {
+    static bool gOnce;
+    if (gOnce) {
+        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 };
+    uint8_t data3[] = {  6, 0, 2,   10,    5, 0, 2, 0, 3, 0xFF };
+    uint8_t data4[] = {  0, 0, 0,   10,    2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
+    uint8_t data5[] = {  1, 0, 0,   10,    2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
+    uint8_t data6[] = {  0, 1, 0,   10,    2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
+    uint8_t data7[] = {  1, 1, 0,   10,    2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
+    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++;
+        const int trimR = *data++;
+        const int expectedSkip = *data++;
+        const int origWidth = *data++;
+        assert_row_width(data, origWidth);
+        int skip = trim_row_left_right(data, origWidth, trimL, trimR);
+        SkASSERT(expectedSkip == skip);
+        int expectedWidth = origWidth - trimL - trimR;
+        assert_row_width(data + skip, expectedWidth);
+    }
+}
+#endif
+
+bool SkAAClip::trimLeftRight() {
+    SkDEBUGCODE(test_trim_row_left_right();)
+
+    if (this->isEmpty()) {
+        return false;
+    }
+    
+    AUTO_AACLIP_VALIDATE(*this);
+
+    const int width = fBounds.width();
+    RunHead* head = fRunHead;
+    YOffset* yoff = head->yoffsets();
+    YOffset* stop = yoff + head->fRowCount;
+    uint8_t* base = head->data();
+
+    int leftZeros = width;
+    int riteZeros = width;
+    while (yoff < stop) {
+        int L, R;
+        count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
+        if (L < leftZeros) {
+            leftZeros = L;
+        }
+        if (R < riteZeros) {
+            riteZeros = R;
+        }
+        if (0 == (leftZeros | riteZeros)) {
+            // no trimming to do
+            return true;
+        }
+        yoff += 1;
+    }
+
+    SkASSERT(leftZeros || riteZeros);
+    if (width == (leftZeros + riteZeros)) {
+        return this->setEmpty();
+    }
+
+    this->validate();
+
+    fBounds.fLeft += leftZeros;
+    fBounds.fRight -= riteZeros;
+    SkASSERT(!fBounds.isEmpty());
+
+    // For now we don't realloc the storage (for time), we just shrink in place
+    // This means we don't have to do any memmoves either, since we can just
+    // play tricks with the yoff->fOffset for each row
+    yoff = head->yoffsets();
+    while (yoff < stop) {
+        uint8_t* row = base + yoff->fOffset;
+        SkDEBUGCODE((void)compute_row_length(row, width);)
+        yoff->fOffset += trim_row_left_right(row, width, leftZeros, riteZeros);
+        SkDEBUGCODE((void)compute_row_length(base + yoff->fOffset, width - leftZeros - riteZeros);)
+        yoff += 1;
+    }
+    return true;
+}
+
+static bool row_is_all_zeros(const uint8_t* row, int width) {
+    SkASSERT(width > 0);
+    do {
+        if (row[1]) {
+            return false;
+        }
+        int n = row[0];
+        SkASSERT(n <= width);
+        width -= n;
+        row += 2;
+    } while (width > 0);
+    SkASSERT(0 == width);
+    return true;
+}
+
+bool SkAAClip::trimTopBottom() {
+    if (this->isEmpty()) {
+        return false;
+    }
+
+    this->validate();
+
+    const int width = fBounds.width();
+    RunHead* head = fRunHead;
+    YOffset* yoff = head->yoffsets();
+    YOffset* stop = yoff + head->fRowCount;
+    const uint8_t* base = head->data();
+
+    //  Look to trim away empty rows from the top.
+    //
+    int skip = 0;
+    while (yoff < stop) {
+        const uint8_t* data = base + yoff->fOffset;
+        if (!row_is_all_zeros(data, width)) {
+            break;
+        }
+        skip += 1;
+        yoff += 1;
+    }
+    SkASSERT(skip <= head->fRowCount);
+    if (skip == head->fRowCount) {
+        return this->setEmpty();
+    }
+    if (skip > 0) {
+        // adjust fRowCount and fBounds.fTop, and slide all the data up
+        // as we remove [skip] number of YOffset entries
+        yoff = head->yoffsets();
+        int dy = yoff[skip - 1].fY + 1;
+        for (int i = skip; i < head->fRowCount; ++i) {
+            SkASSERT(yoff[i].fY >= dy);
+            yoff[i].fY -= dy;
+        }
+        YOffset* dst = head->yoffsets();
+        size_t size = head->fRowCount * sizeof(YOffset) + head->fDataSize;
+        memmove(dst, dst + skip, size - skip * sizeof(YOffset));
+
+        fBounds.fTop += dy;
+        SkASSERT(!fBounds.isEmpty());
+        head->fRowCount -= skip;
+        SkASSERT(head->fRowCount > 0);
+        
+        this->validate();
+        // need to reset this after the memmove
+        base = head->data();
+    }
+
+    //  Look to trim away empty rows from the bottom.
+    //  We know that we have at least one non-zero row, so we can just walk
+    //  backwards without checking for running past the start.
+    //
+    stop = yoff = head->yoffsets() + head->fRowCount;
+    do {
+        yoff -= 1;
+    } while (row_is_all_zeros(base + yoff->fOffset, width));
+    skip = stop - yoff - 1;
+    SkASSERT(skip >= 0 && skip < head->fRowCount);
+    if (skip > 0) {
+        // removing from the bottom is easier than from the top, as we don't
+        // have to adjust any of the Y values, we just have to trim the array
+        memmove(stop - skip, stop, head->fDataSize);
+
+        fBounds.fBottom = fBounds.fTop + yoff->fY + 1;
+        SkASSERT(!fBounds.isEmpty());
+        head->fRowCount -= skip;
+        SkASSERT(head->fRowCount > 0);
+    }
+    this->validate();
+
+    return true;
+}
+
+// can't validate before we're done, since trimming is part of the process of
+// making us valid after the Builder. Since we build from top to bottom, its
+// possible our fBounds.fBottom is bigger than our last scanline of data, so
+// we trim fBounds.fBottom back up.
+//
+// TODO: check for duplicates in X and Y to further compress our data
+//
+bool SkAAClip::trimBounds() {
+    if (this->isEmpty()) {
+        return false;
+    }
+
+    const RunHead* head = fRunHead;
+    const YOffset* yoff = head->yoffsets();
+
+    SkASSERT(head->fRowCount > 0);
+    const YOffset& lastY = yoff[head->fRowCount - 1];
+    SkASSERT(lastY.fY + 1 <= fBounds.height());
+    fBounds.fBottom = fBounds.fTop + lastY.fY + 1;
+    SkASSERT(lastY.fY + 1 == fBounds.height());
+    SkASSERT(!fBounds.isEmpty());
+
+    return this->trimTopBottom() && this->trimLeftRight();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkAAClip::freeRuns() {
+    if (fRunHead) {
+        SkASSERT(fRunHead->fRefCnt >= 1);
+        if (1 == sk_atomic_dec(&fRunHead->fRefCnt)) {
+            sk_free(fRunHead);
+        }
+    }
+}
+
+SkAAClip::SkAAClip() {
+    fBounds.setEmpty();
+    fRunHead = NULL;
+}
+
+SkAAClip::SkAAClip(const SkAAClip& src) {
+    SkDEBUGCODE(fBounds.setEmpty();)    // need this for validate
+    fRunHead = NULL;
+    *this = src;
+}
+
+SkAAClip::~SkAAClip() {
+    this->freeRuns();
+}
+
+SkAAClip& SkAAClip::operator=(const SkAAClip& src) {
+    AUTO_AACLIP_VALIDATE(*this);
+    src.validate();
+
+    if (this != &src) {
+        this->freeRuns();
+        fBounds = src.fBounds;
+        fRunHead = src.fRunHead;
+        if (fRunHead) {
+            sk_atomic_inc(&fRunHead->fRefCnt);
+        }
+    }
+    return *this;
+}
+
+bool operator==(const SkAAClip& a, const SkAAClip& b) {
+    a.validate();
+    b.validate();
+
+    if (&a == &b) {
+        return true;
+    }
+    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;
+    }
+
+    // now we insist that both are complex (but different ptrs)
+    if (!a.fRunHead || !b.fRunHead) {
+        return false;
+    }
+
+    return  ah->fRowCount == bh->fRowCount &&
+            ah->fDataSize == bh->fDataSize &&
+            !memcmp(ah->data(), bh->data(), ah->fDataSize);
+}
+
+void SkAAClip::swap(SkAAClip& other) {
+    AUTO_AACLIP_VALIDATE(*this);
+    other.validate();
+
+    SkTSwap(fBounds, other.fBounds);
+    SkTSwap(fRunHead, other.fRunHead);
+}
+
+bool SkAAClip::set(const SkAAClip& src) {
+    *this = src;
+    return !this->isEmpty();
+}
+
+bool SkAAClip::setEmpty() {
+    this->freeRuns();
+    fBounds.setEmpty();
+    fRunHead = NULL;
+    return false;
+}
+
+bool SkAAClip::setRect(const SkIRect& bounds) {
+    if (bounds.isEmpty()) {
+        return this->setEmpty();
+    }
+
+    AUTO_AACLIP_VALIDATE(*this);
+
+#if 0
+    SkRect r;
+    r.set(bounds);
+    SkPath path;
+    path.addRect(r);
+    return this->setPath(path);
+#else
+    this->freeRuns();
+    fBounds = bounds;
+    fRunHead = RunHead::AllocRect(bounds);
+    SkASSERT(!this->isEmpty());
+    return true;
+#endif
+}
+
+bool SkAAClip::setRect(const SkRect& r, bool doAA) {
+    if (r.isEmpty()) {
+        return this->setEmpty();
+    }
+
+    AUTO_AACLIP_VALIDATE(*this);
+
+    // TODO: special case this
+
+    SkPath path;
+    path.addRect(r);
+    return this->setPath(path, NULL, doAA);
+}
+
+static void append_run(SkTDArray<uint8_t>& array, uint8_t value, int count) {
+    SkASSERT(count >= 0);
+    while (count > 0) {
+        int n = count;
+        if (n > 255) {
+            n = 255;
+        }
+        uint8_t* data = array.append(2);
+        data[0] = n;
+        data[1] = value;
+        count -= n;
+    }
+}
+
+bool SkAAClip::setRegion(const SkRegion& rgn) {
+    if (rgn.isEmpty()) {
+        return this->setEmpty();
+    }
+    if (rgn.isRect()) {
+        return this->setRect(rgn.getBounds());
+    }
+
+#if 0
+    SkAAClip clip;
+    SkRegion::Iterator iter(rgn);
+    for (; !iter.done(); iter.next()) {
+        clip.op(iter.rect(), SkRegion::kUnion_Op);
+    }
+    this->swap(clip);
+    return !this->isEmpty();
+#else    
+    const SkIRect& bounds = rgn.getBounds();
+    const int offsetX = bounds.fLeft;
+    const int offsetY = bounds.fTop;
+
+    SkTDArray<YOffset> yArray;
+    SkTDArray<uint8_t> xArray;
+
+    yArray.setReserve(SkMin32(bounds.height(), 1024));
+    xArray.setReserve(SkMin32(bounds.width() * 128, 64 * 1024));
+
+    SkRegion::Iterator iter(rgn);
+    int prevRight = 0;
+    int prevBot = 0;
+    YOffset* currY = NULL;
+
+    for (; !iter.done(); iter.next()) {
+        const SkIRect& r = iter.rect();
+        SkASSERT(bounds.contains(r));
+
+        int bot = r.fBottom - offsetY;
+        SkASSERT(bot >= prevBot);
+        if (bot > prevBot) {
+            if (currY) {
+                // flush current row
+                append_run(xArray, 0, bounds.width() - prevRight);
+            }
+            // did we introduce an empty-gap from the prev row?
+            int top = r.fTop - offsetY;
+            if (top > prevBot) {
+                currY = yArray.append();
+                currY->fY = top - 1;
+                currY->fOffset = xArray.count();
+                append_run(xArray, 0, bounds.width());
+            }
+            // create a new record for this Y value
+            currY = yArray.append();
+            currY->fY = bot - 1;
+            currY->fOffset = xArray.count();
+            prevRight = 0;
+            prevBot = bot;
+        }
+
+        int x = r.fLeft - offsetX;
+        append_run(xArray, 0, x - prevRight);
+
+        int w = r.fRight - r.fLeft;
+        append_run(xArray, 0xFF, w);
+        prevRight = x + w;
+        SkASSERT(prevRight <= bounds.width());
+    }
+    // flush last row
+    append_run(xArray, 0, bounds.width() - prevRight);
+
+    // now pack everything into a RunHead
+    RunHead* head = RunHead::Alloc(yArray.count(), xArray.bytes());
+    memcpy(head->yoffsets(), yArray.begin(), yArray.bytes());
+    memcpy(head->data(), xArray.begin(), xArray.bytes());
+
+    this->setEmpty();
+    fBounds = bounds;
+    fRunHead = head;
+    this->validate();
+    return true;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const uint8_t* SkAAClip::findRow(int y, int* lastYForRow) const {
+    SkASSERT(fRunHead);
+
+    if (!y_in_rect(y, fBounds)) {
+        return NULL;
+    }
+    y -= fBounds.y();  // our yoffs values are relative to the top
+
+    const YOffset* yoff = fRunHead->yoffsets();
+    while (yoff->fY < y) {
+        yoff += 1;
+        SkASSERT(yoff - fRunHead->yoffsets() < fRunHead->fRowCount);
+    }
+
+    if (lastYForRow) {
+        *lastYForRow = fBounds.y() + yoff->fY;
+    }
+    return fRunHead->data() + yoff->fOffset;
+}
+
+const uint8_t* SkAAClip::findX(const uint8_t data[], int x, int* initialCount) const {
+    SkASSERT(x_in_rect(x, fBounds));
+    x -= fBounds.x();
+
+    // first skip up to X
+    for (;;) {
+        int n = data[0];
+        if (x < n) {
+            *initialCount = n - x;
+            break;
+        }
+        data += 2;
+        x -= n;
+    }
+    return data;
+}
+
+bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
+    if (this->isEmpty()) {
+        return false;
+    }
+    if (!fBounds.contains(left, top, right, bottom)) {
+        return false;
+    }
+#if 0
+    if (this->isRect()) {
+        return true;
+    }
+#endif
+
+    int lastY;
+    const uint8_t* row = this->findRow(top, &lastY);
+    if (lastY < bottom) {
+        return false;
+    }
+    // now just need to check in X
+    int count;
+    row = this->findX(row, left, &count);
+#if 0
+    return count >= (right - left) && 0xFF == row[1];
+#else
+    int rectWidth = right - left;
+    while (0xFF == row[1]) {
+        if (count >= rectWidth) {
+            return true;
+        }
+        rectWidth -= count;
+        row += 2;
+        count = row[0];
+    }
+    return false;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAAClip::Builder {
+    SkIRect fBounds;
+    struct Row {
+        int fY;
+        int fWidth;
+        SkTDArray<uint8_t>* fData;
+    };
+    SkTDArray<Row>  fRows;
+    Row* fCurrRow;
+    int fPrevY;
+    int fWidth;
+    int fMinY;
+
+public:
+    Builder(const SkIRect& bounds) : fBounds(bounds) {
+        fPrevY = -1;
+        fWidth = bounds.width();
+        fCurrRow = NULL;
+        fMinY = bounds.fTop;
+    }
+
+    ~Builder() {
+        Row* row = fRows.begin();
+        Row* stop = fRows.end();
+        while (row < stop) {
+            delete row->fData;
+            row += 1;
+        }
+    }
+
+    const SkIRect& getBounds() const { return fBounds; }
+
+    void addRun(int x, int y, U8CPU alpha, int count) {
+        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);
+            fPrevY = y;
+            row = this->flushRow(true);
+            row->fY = y;
+            row->fWidth = 0;
+            SkASSERT(row->fData);
+            SkASSERT(0 == row->fData->count());
+            fCurrRow = row;
+        }
+
+        SkASSERT(row->fWidth <= x);
+        SkASSERT(row->fWidth < fBounds.width());
+
+        SkTDArray<uint8_t>& data = *row->fData;
+
+        int gap = x - row->fWidth;
+        if (gap) {
+            AppendRun(data, 0, gap);
+            row->fWidth += gap;
+            SkASSERT(row->fWidth < fBounds.width());
+        }
+
+        AppendRun(data, alpha, count);
+        row->fWidth += count;
+        SkASSERT(row->fWidth <= fBounds.width());
+    }
+
+    void addColumn(int x, int y, U8CPU alpha, int height) {
+        SkASSERT(fBounds.contains(x, y + height - 1));
+
+        this->addRun(x, y, alpha, 1);
+        this->flushRowH(fCurrRow);
+        y -= fBounds.fTop;
+        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);
+
+        // we assum the rect must be all we'll see for these scanlines
+        // so we ensure our row goes all the way to our right
+        this->flushRowH(fCurrRow);
+
+        y -= fBounds.fTop;
+        SkASSERT(y == fCurrRow->fY);
+        fCurrRow->fY = y + height - 1;
+    }
+
+    void addAntiRectRun(int x, int y, int width, int height,
+                        SkAlpha leftAlpha, SkAlpha rightAlpha) {
+        SkASSERT(fBounds.contains(x + width - 1 +
+                 (leftAlpha > 0 ? 1 : 0) + (rightAlpha > 0 ? 1 : 0),
+                 y + height - 1));
+        SkASSERT(width >= 0);
+
+        // Conceptually we're always adding 3 runs, but we should
+        // merge or omit them if possible.
+        if (leftAlpha == 0xFF) {
+            width++;
+        } else if (leftAlpha > 0) {
+          this->addRun(x++, y, leftAlpha, 1);
+        }
+        if (rightAlpha == 0xFF) {
+            width++;
+        }
+        if (width > 0) {
+            this->addRun(x, y, 0xFF, width);
+        }
+        if (rightAlpha > 0 && rightAlpha < 255) {
+            this->addRun(x + width, y, rightAlpha, 1);
+        }
+
+        // we assume the rect must be all we'll see for these scanlines
+        // so we ensure our row goes all the way to our right
+        this->flushRowH(fCurrRow);
+
+        y -= fBounds.fTop;
+        SkASSERT(y == fCurrRow->fY);
+        fCurrRow->fY = y + height - 1;
+    }
+
+    bool finish(SkAAClip* target) {
+        this->flushRow(false);
+
+        const Row* row = fRows.begin();
+        const Row* stop = fRows.end();
+
+        size_t dataSize = 0;    
+        while (row < stop) {
+            dataSize += row->fData->count();
+            row += 1;
+        }
+
+        if (0 == dataSize) {
+            return target->setEmpty();
+        }
+
+        SkASSERT(fMinY >= fBounds.fTop);
+        SkASSERT(fMinY < fBounds.fBottom);
+        int adjustY = fMinY - fBounds.fTop;
+        fBounds.fTop = fMinY;
+
+        RunHead* head = RunHead::Alloc(fRows.count(), dataSize);
+        YOffset* yoffset = head->yoffsets();
+        uint8_t* data = head->data();
+        uint8_t* baseData = data;
+
+        row = fRows.begin();
+        SkDEBUGCODE(int prevY = row->fY - 1;)
+        while (row < stop) {
+            SkASSERT(prevY < row->fY);  // must be monotonic
+            SkDEBUGCODE(prevY = row->fY);
+
+            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
+            size_t bytesNeeded = compute_row_length(data, fBounds.width());
+            SkASSERT(bytesNeeded == n);
+#endif
+            data += n;
+            
+            row += 1;
+        }
+
+        target->freeRuns();
+        target->fBounds = fBounds;
+        target->fRunHead = head;
+        return target->trimBounds();
+    }
+
+    void dump() {
+        this->validate();
+        int y;
+        for (y = 0; y < fRows.count(); ++y) {
+            const Row& row = fRows[y];
+            SkDebugf("Y:%3d W:%3d", row.fY, row.fWidth);
+            const SkTDArray<uint8_t>& data = *row.fData;
+            int count = data.count();
+            SkASSERT(!(count & 1));
+            const uint8_t* ptr = data.begin();
+            for (int x = 0; x < count; x += 2) {
+                SkDebugf(" [%3d:%02X]", ptr[0], ptr[1]);
+                ptr += 2;
+            }
+            SkDebugf("\n");
+        }
+    }
+
+    void validate() {
+#ifdef SK_DEBUG
+        int prevY = -1;
+        for (int i = 0; i < fRows.count(); ++i) {
+            const Row& row = fRows[i];
+            SkASSERT(prevY < row.fY);
+            SkASSERT(fWidth == row.fWidth);
+            int count = row.fData->count();
+            const uint8_t* ptr = row.fData->begin();
+            SkASSERT(!(count & 1));
+            int w = 0;
+            for (int x = 0; x < count; x += 2) {
+                int n = ptr[0];
+                SkASSERT(n > 0);
+                w += n;
+                SkASSERT(w <= fWidth);
+                ptr += 2;
+            }
+            SkASSERT(w == fWidth);
+            prevY = row.fY;
+        }
+#endif
+    }
+
+    // only called by BuilderBlitter
+    void setMinY(int y) {
+        fMinY = y;
+    }
+
+private:
+    void flushRowH(Row* row) {
+        // flush current row if needed
+        if (row->fWidth < fWidth) {
+            AppendRun(*row->fData, 0, fWidth - row->fWidth);
+            row->fWidth = fWidth;
+        }
+    }
+
+    Row* flushRow(bool readyForAnother) {
+        Row* next = NULL;
+        int count = fRows.count();
+        if (count > 0) {
+            this->flushRowH(&fRows[count - 1]);
+        }
+        if (count > 1) {
+            // are our last two runs the same?
+            Row* prev = &fRows[count - 2];
+            Row* curr = &fRows[count - 1];
+            SkASSERT(prev->fWidth == fWidth);
+            SkASSERT(curr->fWidth == fWidth);
+            if (*prev->fData == *curr->fData) {
+                prev->fY = curr->fY;
+                if (readyForAnother) {
+                    curr->fData->rewind();
+                    next = curr;
+                } else {
+                    delete curr->fData;
+                    fRows.removeShuffle(count - 1);
+                }
+            } else {
+                if (readyForAnother) {
+                    next = fRows.append();
+                    next->fData = new SkTDArray<uint8_t>;
+                }
+            }
+        } else {
+            if (readyForAnother) {
+                next = fRows.append();
+                next->fData = new SkTDArray<uint8_t>;
+            }
+        }
+        return next;
+    }
+
+    static void AppendRun(SkTDArray<uint8_t>& data, U8CPU alpha, int count) {
+        do {
+            int n = count;
+            if (n > 255) {
+                n = 255;
+            }
+            uint8_t* ptr = data.append(2);
+            ptr[0] = n;
+            ptr[1] = alpha;
+            count -= n;
+        } while (count > 0);
+    }
+};
+
+class SkAAClip::BuilderBlitter : public SkBlitter {
+    int fLastY;
+
+    /*
+        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) {
+        SkASSERT(y >= fLastY);
+        if (fLastY > -SK_MaxS32) {
+            int gap = y - fLastY;
+            if (gap > 1) {
+                fBuilder->addRun(fLeft, y - 1, 0, fRight - fLeft);
+            }
+        }
+        fLastY = y;
+    }
+
+public:
+
+    BuilderBlitter(Builder* builder) {
+        fBuilder = builder;
+        fLeft = builder->getBounds().fLeft;
+        fRight = builder->getBounds().fRight;
+        fMinY = SK_MaxS32;
+        fLastY = -SK_MaxS32;    // sentinel
+    }
+
+    void finish() {
+        if (fMinY < SK_MaxS32) {
+            fBuilder->setMinY(fMinY);
+        }
+    }
+
+    /**
+       Must evaluate clips in scan-line order, so don't want to allow blitV(),
+       but an AAClip can be clipped down to a single pixel wide, so we
+       must support it (given AntiRect semantics: minimum width is 2).
+       Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
+       any failure cases that misses may have minor artifacts.
+    */
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
+        this->recordMinY(y);
+        fBuilder->addColumn(x, y, alpha, height);
+        fLastY = y + height - 1;
+    }
+
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+        this->recordMinY(y);
+        this->checkForYGap(y);
+        fBuilder->addRectRun(x, y, width, height);
+        fLastY = y + height - 1;
+    }
+
+    virtual void blitAntiRect(int x, int y, int width, int height,
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
+        this->recordMinY(y);
+        this->checkForYGap(y);
+        fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
+        fLastY = y + height - 1;
+    }
+
+    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE
+        { unexpected(); }
+
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE {
+        return NULL;
+    }
+
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE {
+        this->recordMinY(y);
+        this->checkForYGap(y);
+        fBuilder->addRun(x, y, 0xFF, width);
+    }
+
+    virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
+                           const int16_t runs[]) SK_OVERRIDE {
+        this->recordMinY(y);
+        this->checkForYGap(y);
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                return;
+            }
+
+            // The supersampler's buffer can be the width of the device, so
+            // we may have to trim the run to our bounds. If so, we assert that
+            // the extra spans are always alpha==0
+            int localX = x;
+            int localCount = count;
+            if (x < fLeft) {
+                SkASSERT(0 == *alpha);
+                int gap = fLeft - x;
+                SkASSERT(gap <= count);
+                localX += gap;
+                localCount -= gap;
+            }
+            int right = x + count;
+            if (right > fRight) {
+                SkASSERT(0 == *alpha);
+                localCount -= right - fRight;
+                SkASSERT(localCount >= 0);
+            }
+            
+            if (localCount) {
+                fBuilder->addRun(localX, y, *alpha, localCount);
+            }
+            // Next run
+            runs += count;
+            alpha += count;
+            x += count;
+        }
+    }
+
+private:
+    Builder* fBuilder;
+    int      fLeft; // cache of builder's bounds' left edge
+    int      fRight;
+    int      fMinY;
+
+    /*
+     *  We track this, in case the scan converter skipped some number of
+     *  scanlines at the (relative to the bounds it was given). This allows
+     *  the builder, during its finish, to trip its bounds down to the "real"
+     *  top.
+     */
+    void recordMinY(int y) {
+        if (y < fMinY) {
+            fMinY = y;
+        }
+    }
+
+    void unexpected() {
+        SkDebugf("---- did not expect to get called here");
+        sk_throw();
+    }
+};
+
+bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip, bool doAA) {
+    AUTO_AACLIP_VALIDATE(*this);
+
+    if (clip && clip->isEmpty()) {
+        return this->setEmpty();
+    }
+
+    SkIRect ibounds;
+    path.getBounds().roundOut(&ibounds);
+
+    SkRegion tmpClip;
+    if (NULL == clip) {
+        tmpClip.setRect(ibounds);
+        clip = &tmpClip;
+    }
+    
+    if (path.isInverseFillType()) {
+        ibounds = clip->getBounds();
+    } else {
+        if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
+            return this->setEmpty();
+        }
+    }
+
+    Builder        builder(ibounds);
+    BuilderBlitter blitter(&builder);
+
+    if (doAA) {
+        SkScan::AntiFillPath(path, *clip, &blitter, true);
+    } else {
+        SkScan::FillPath(path, *clip, &blitter);
+    }
+
+    blitter.finish();
+    return builder.finish(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
+                        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) {
+    // Multiply
+    return SkMulDiv255Round(alphaA, alphaB);
+}
+
+static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
+    // SrcOver
+    return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
+}
+
+static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
+    // SrcOut
+    return SkMulDiv255Round(alphaA, 0xFF - alphaB);
+}
+
+static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
+    // XOR
+    return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
+}
+
+static AlphaProc find_alpha_proc(SkRegion::Op op) {
+    switch (op) {
+        case SkRegion::kIntersect_Op:
+            return sectAlphaProc;
+        case SkRegion::kDifference_Op:
+            return diffAlphaProc;
+        case SkRegion::kUnion_Op:
+            return unionAlphaProc;
+        case SkRegion::kXOR_Op:
+            return xorAlphaProc;
+        default:
+            SkDEBUGFAIL("unexpected region op");
+            return sectAlphaProc;
+    }
+}
+
+static const uint8_t gEmptyRow[] = {
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+};
+
+class RowIter {
+public:
+    RowIter(const uint8_t* row, const SkIRect& bounds) {
+        fRow = row;
+        fLeft = bounds.fLeft;
+        fBoundsRight = bounds.fRight;
+        if (row) {
+            fRight = bounds.fLeft + row[0];
+            SkASSERT(fRight <= fBoundsRight);
+            fAlpha = row[1];
+            fDone = false;
+        } else {
+            fDone = true;
+            fRight = kMaxInt32;
+            fAlpha = 0;
+        }
+    }
+
+    bool done() const { return fDone; }
+    int left() const { return fLeft; }
+    int right() const { return fRight; }
+    U8CPU alpha() const { return fAlpha; }
+    void next() {
+        if (!fDone) {
+            fLeft = fRight;
+            if (fRight == fBoundsRight) {
+                fDone = true;
+                fRight = kMaxInt32;
+                fAlpha = 0;
+            } else {
+                fRow += 2;
+                fRight += fRow[0];
+                fAlpha = fRow[1];
+                SkASSERT(fRight <= fBoundsRight);
+            }
+        }
+    }
+
+private:
+    const uint8_t*  fRow;
+    int             fLeft;
+    int             fRight;
+    int             fBoundsRight;
+    bool            fDone;
+    uint8_t         fAlpha;
+};
+
+static void adjust_row(RowIter& iter, int& leftA, int& riteA, int rite) {
+    if (rite == riteA) {
+        iter.next();
+        leftA = iter.left();
+        riteA = iter.right();
+    }
+}
+
+static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
+    SkASSERT(min < max);
+    SkASSERT(boundsMin < boundsMax);
+    if (min >= boundsMax || max <= boundsMin) {
+        return false;
+    }
+    if (min < boundsMin) {
+        min = boundsMin;
+    }
+    if (max > boundsMax) {
+        max = boundsMax;
+    }
+    return true;
+}
+
+static void operatorX(SkAAClip::Builder& builder, int lastY,
+                      RowIter& iterA, RowIter& iterB,
+                      AlphaProc proc, const SkIRect& bounds) {
+    int leftA = iterA.left();
+    int riteA = iterA.right();
+    int leftB = iterB.left();
+    int riteB = iterB.right();
+
+    int prevRite = bounds.fLeft;
+
+    do {
+        U8CPU alphaA = 0;
+        U8CPU alphaB = 0;
+        int left, rite;
+ 
+        if (leftA < leftB) {
+            left = leftA;
+            alphaA = iterA.alpha();
+            if (riteA <= leftB) {
+                rite = riteA;
+            } else {
+                rite = leftA = leftB;
+            }
+        } else if (leftB < leftA) {
+            left = leftB;
+            alphaB = iterB.alpha();
+            if (riteB <= leftA) {
+                rite = riteB;
+            } else {
+                rite = leftB = leftA;
+            }
+        } else {
+            left = leftA;   // or leftB, since leftA == leftB
+            rite = leftA = leftB = SkMin32(riteA, riteB);
+            alphaA = iterA.alpha();
+            alphaB = iterB.alpha();
+        }
+
+        if (left >= bounds.fRight) {
+            break;
+        }
+        if (rite > bounds.fRight) {
+            rite = bounds.fRight;
+        }
+
+        if (left >= bounds.fLeft) {
+            SkASSERT(rite > left);
+            builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
+            prevRite = rite;
+        }
+
+        adjust_row(iterA, leftA, riteA, rite);
+        adjust_row(iterB, leftB, riteB, rite);
+    } while (!iterA.done() || !iterB.done());
+
+    if (prevRite < bounds.fRight) {
+        builder.addRun(prevRite, lastY, 0, bounds.fRight - prevRite);
+    }
+}
+
+static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA, int bot) {
+    if (bot == botA) {
+        iter.next();
+        topA = botA;
+        SkASSERT(botA == iter.top());
+        botA = iter.bottom();
+    }
+}
+
+static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
+                     const SkAAClip& B, SkRegion::Op op) {
+    AlphaProc proc = find_alpha_proc(op);
+    const SkIRect& bounds = builder.getBounds();
+
+    SkAAClip::Iter iterA(A);
+    SkAAClip::Iter iterB(B);
+
+    SkASSERT(!iterA.done());
+    int topA = iterA.top();
+    int botA = iterA.bottom();
+    SkASSERT(!iterB.done());
+    int topB = iterB.top();
+    int botB = iterB.bottom();
+
+    do {
+        const uint8_t* rowA = NULL;
+        const uint8_t* rowB = NULL;
+        int top, bot;
+
+        if (topA < topB) {
+            top = topA;
+            rowA = iterA.data();
+            if (botA <= topB) {
+                bot = botA;
+            } else {
+                bot = topA = topB;
+            }
+            
+        } else if (topB < topA) {
+            top = topB;
+            rowB = iterB.data();
+            if (botB <= topA) {
+                bot = botB;
+            } else {
+                bot = topB = topA;
+            }
+        } else {
+            top = topA;   // or topB, since topA == topB
+            bot = topA = topB = SkMin32(botA, botB);
+            rowA = iterA.data();
+            rowB = iterB.data();
+        }
+
+        if (top >= bounds.fBottom) {
+            break;
+        }
+
+        if (bot > bounds.fBottom) {
+            bot = bounds.fBottom;
+        }
+        SkASSERT(top < bot);
+
+        if (!rowA && !rowB) {
+            builder.addRun(bounds.fLeft, bot - 1, 0, bounds.width());
+        } else if (top >= bounds.fTop) {
+            SkASSERT(bot <= bounds.fBottom);
+            RowIter rowIterA(rowA, rowA ? A.getBounds() : bounds);
+            RowIter rowIterB(rowB, rowB ? B.getBounds() : bounds);
+            operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
+        }
+
+        adjust_iter(iterA, topA, botA, bot);
+        adjust_iter(iterB, topB, botB, bot);
+    } while (!iterA.done() || !iterB.done());
+}
+
+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;
+    }
+
+    bool a_empty = clipA->isEmpty();
+    bool b_empty = clipB->isEmpty();
+
+    SkIRect bounds;
+    switch (op) {
+        case SkRegion::kDifference_Op:
+            if (a_empty) {
+                return this->setEmpty();
+            }
+            if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
+                return this->set(*clipA);
+            }
+            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) {
+                return this->set(*clipB);
+            }
+            if (b_empty) {
+                return this->set(*clipA);
+            }
+            bounds = clipA->fBounds;
+            bounds.join(clipB->fBounds);
+            break;
+
+        default:
+            SkDEBUGFAIL("unknown region op");
+            return !this->isEmpty();
+    }
+
+    SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
+    SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
+
+    Builder builder(bounds);
+    operateY(builder, *clipA, *clipB, op);
+
+    return builder.finish(this);
+}
+
+/*
+ *  It can be expensive to build a local aaclip before applying the op, so
+ *  we first see if we can restrict the bounds of new rect to our current
+ *  bounds, or note that the new rect subsumes our current clip.
+ */
+
+bool SkAAClip::op(const SkIRect& rOrig, SkRegion::Op op) {
+    SkIRect        rStorage;
+    const SkIRect* r = &rOrig;
+
+    switch (op) {
+        case SkRegion::kIntersect_Op:
+            if (!rStorage.intersect(rOrig, fBounds)) {
+                // no overlap, so we're empty
+                return this->setEmpty();
+            }
+            if (rStorage == fBounds) {
+                // we were wholly inside the rect, no change
+                return !this->isEmpty();
+            }
+            if (this->quickContains(rStorage)) {
+                // the intersection is wholly inside us, we're a rect
+                return this->setRect(rStorage);
+            }
+            r = &rStorage;   // use the intersected bounds
+            break;
+        case SkRegion::kDifference_Op:
+            break;
+        case SkRegion::kUnion_Op:
+            if (rOrig.contains(fBounds)) {
+                return this->setRect(rOrig);
+            }
+            break;
+        default:
+            break;
+    }
+
+    SkAAClip clip;
+    clip.setRect(*r);
+    return this->op(*this, clip, op);
+}
+
+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();
+            }
+            r = &rStorage;   // use the intersected bounds
+            break;
+        case SkRegion::kUnion_Op:
+            if (rOrig.contains(boundsStorage)) {
+                return this->setRect(rOrig);
+            }
+            break;
+        default:
+            break;
+    }
+    
+    SkAAClip clip;
+    clip.setRect(*r, doAA);
+    return this->op(*this, clip, op);
+}
+
+bool SkAAClip::op(const SkAAClip& clip, SkRegion::Op op) {
+    return this->op(*this, clip, op);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkAAClip::translate(int dx, int dy, SkAAClip* dst) const {
+    if (NULL == dst) {
+        return !this->isEmpty();
+    }
+    
+    if (this->isEmpty()) {
+        return dst->setEmpty();
+    }
+    
+    if (this != dst) {
+        sk_atomic_inc(&fRunHead->fRefCnt);
+        dst->fRunHead = fRunHead;
+        dst->fBounds = fBounds;
+    }
+    dst->fBounds.offset(dx, dy);
+    return true;
+}
+
+static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
+                               const uint8_t* SK_RESTRICT row,
+                               int width) {
+    while (width > 0) {
+        int n = row[0];
+        SkASSERT(width >= n);
+        memset(mask, row[1], n);
+        mask += n;
+        row += 2;
+        width -= n;
+    }
+    SkASSERT(0 == width);
+}
+
+void SkAAClip::copyToMask(SkMask* mask) const {
+    mask->fFormat = SkMask::kA8_Format;
+    if (this->isEmpty()) {
+        mask->fBounds.setEmpty();
+        mask->fImage = NULL;
+        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 {
+            expand_row_to_mask(dst, iter.data(), width);
+            dst += mask->fRowBytes;
+        } while (++y < iter.bottom());
+        iter.next();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void expandToRuns(const uint8_t* SK_RESTRICT data, int initialCount, int width,
+                         int16_t* SK_RESTRICT runs, SkAlpha* SK_RESTRICT aa) {
+    // we don't read our initial n from data, since the caller may have had to
+    // clip it, hence the initialCount parameter.
+    int n = initialCount;
+    for (;;) {
+        if (n > width) {
+            n = width;
+        }
+        SkASSERT(n > 0);
+        runs[0] = n;
+        runs += n;
+
+        aa[0] = data[1];
+        aa += n;
+        
+        data += 2;
+        width -= n;
+        if (0 == width) {
+            break;
+        }
+        // load the next count
+        n = data[0];
+    }
+    runs[0] = 0;    // sentinel
+}
+
+SkAAClipBlitter::~SkAAClipBlitter() {
+    sk_free(fScanlineScratch);
+}
+
+void SkAAClipBlitter::ensureRunsAndAA() {
+    if (NULL == fScanlineScratch) {
+        // add 1 so we can store the terminating run count of 0
+        int count = fAAClipBounds.width() + 1;
+        // we use this either for fRuns + fAA, or a scaline of a mask
+        // which may be as deep as 32bits
+        fScanlineScratch = sk_malloc_throw(count * sizeof(SkPMColor));
+        fRuns = (int16_t*)fScanlineScratch;
+        fAA = (SkAlpha*)(fRuns + count);
+    }
+}
+
+void SkAAClipBlitter::blitH(int x, int y, int width) {
+    SkASSERT(width > 0);
+    SkASSERT(fAAClipBounds.contains(x, y));
+    SkASSERT(fAAClipBounds.contains(x + width  - 1, y));
+
+    int lastY;
+    const uint8_t* row = fAAClip->findRow(y, &lastY);
+    int initialCount;
+    row = fAAClip->findX(row, x, &initialCount);
+
+    if (initialCount >= width) {
+        SkAlpha alpha = row[1];
+        if (0 == alpha) {
+            return;
+        }
+        if (0xFF == alpha) {
+            fBlitter->blitH(x, y, width);
+            return;
+        }
+    }
+
+    this->ensureRunsAndAA();
+    expandToRuns(row, initialCount, width, fRuns, fAA);
+
+    fBlitter->blitAntiH(x, y, fAA, fRuns);
+}
+
+static void merge(const uint8_t* SK_RESTRICT row, int rowN,
+                  const SkAlpha* SK_RESTRICT srcAA,
+                  const int16_t* SK_RESTRICT srcRuns,
+                  SkAlpha* SK_RESTRICT dstAA,
+                  int16_t* SK_RESTRICT dstRuns,
+                  int width) {
+    SkDEBUGCODE(int accumulated = 0;)
+    int srcN = srcRuns[0];
+    // do we need this check?
+    if (0 == srcN) {
+        return;
+    }
+
+    for (;;) {
+        SkASSERT(rowN > 0);
+        SkASSERT(srcN > 0);
+
+        unsigned newAlpha = SkMulDiv255Round(srcAA[0], row[1]);
+        int minN = SkMin32(srcN, rowN);
+        dstRuns[0] = minN;
+        dstRuns += minN;
+        dstAA[0] = newAlpha;
+        dstAA += minN;
+
+        if (0 == (srcN -= minN)) {
+            srcN = srcRuns[0];  // refresh
+            srcRuns += srcN;
+            srcAA += srcN;
+            srcN = srcRuns[0];  // reload
+            if (0 == srcN) {
+                break;
+            }
+        }
+        if (0 == (rowN -= minN)) {
+            row += 2;
+            rowN = row[0];  // reload
+        }
+        
+        SkDEBUGCODE(accumulated += minN;)
+        SkASSERT(accumulated <= width);
+    }
+    dstRuns[0] = 0;
+}
+
+void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
+                                const int16_t runs[]) {
+    int lastY;
+    const uint8_t* row = fAAClip->findRow(y, &lastY);
+    int initialCount;
+    row = fAAClip->findX(row, x, &initialCount);
+
+    this->ensureRunsAndAA();
+
+    merge(row, initialCount, aa, runs, fAA, fRuns, fAAClipBounds.width());
+    fBlitter->blitAntiH(x, y, fAA, fRuns);
+}
+
+void SkAAClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    if (fAAClip->quickContains(x, y, x + 1, y + height)) {
+        fBlitter->blitV(x, y, height, alpha);
+        return;
+    }
+
+    for (;;) {
+        int lastY;
+        const uint8_t* row = fAAClip->findRow(y, &lastY);
+        int dy = lastY - y + 1;
+        if (dy > height) {
+            dy = height;
+        }
+        height -= dy;
+
+        int initialCount;
+        row = fAAClip->findX(row, x, &initialCount);
+        SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
+        if (newAlpha) {
+            fBlitter->blitV(x, y, dy, newAlpha);
+        }
+        SkASSERT(height >= 0);
+        if (height <= 0) {
+            break;
+        }
+        y = lastY + 1;
+    }
+}
+
+void SkAAClipBlitter::blitRect(int x, int y, int width, int height) {
+    if (fAAClip->quickContains(x, y, x + width, y + height)) {
+        fBlitter->blitRect(x, y, width, height);
+        return;
+    }
+
+    while (--height >= 0) {
+        this->blitH(x, y, width);
+        y += 1;
+    }
+}
+
+typedef void (*MergeAAProc)(const void* src, int width, const uint8_t* row,
+                            int initialRowCount, void* dst);
+
+static void small_memcpy(void* dst, const void* src, size_t n) {
+    memcpy(dst, src, n);
+}
+
+static void small_bzero(void* dst, size_t n) {
+    sk_bzero(dst, n);
+}
+
+static inline uint8_t mergeOne(uint8_t value, unsigned alpha) {
+    return SkMulDiv255Round(value, alpha);
+}
+static inline uint16_t mergeOne(uint16_t value, unsigned alpha) {
+    unsigned r = SkGetPackedR16(value);
+    unsigned g = SkGetPackedG16(value);
+    unsigned b = SkGetPackedB16(value);
+    return SkPackRGB16(SkMulDiv255Round(r, alpha),
+                       SkMulDiv255Round(r, alpha),
+                       SkMulDiv255Round(r, alpha));
+}
+static inline SkPMColor mergeOne(SkPMColor value, unsigned alpha) {
+    unsigned a = SkGetPackedA32(value);
+    unsigned r = SkGetPackedR32(value);
+    unsigned g = SkGetPackedG32(value);
+    unsigned b = SkGetPackedB32(value);
+    return SkPackARGB32(SkMulDiv255Round(a, alpha),
+                        SkMulDiv255Round(r, alpha),
+                        SkMulDiv255Round(g, alpha),
+                        SkMulDiv255Round(b, alpha));
+}
+
+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) {
+            small_memcpy(dst, src, n * sizeof(T));
+        } else if (0 == rowA) {
+            small_bzero(dst, n * sizeof(T));
+        } else {
+            for (int i = 0; i < n; ++i) {
+                dst[i] = mergeOne(src[i], rowA);
+            }
+        }
+        
+        if (0 == (srcN -= n)) {
+            break;
+        }
+        
+        src += n;
+        dst += n;
+        
+        SkASSERT(rowN == n);
+        row += 2;
+        rowN = row[0];
+    }
+}
+
+static MergeAAProc find_merge_aa_proc(SkMask::Format format) {
+    switch (format) {
+        case SkMask::kBW_Format:
+            SkDEBUGFAIL("unsupported");
+            return NULL;
+        case SkMask::kA8_Format:
+        case SkMask::k3D_Format: {
+            void (*proc8)(const uint8_t*, int, const uint8_t*, int, uint8_t*) = mergeT;
+            return (MergeAAProc)proc8;
+        }
+        case SkMask::kLCD16_Format: {
+            void (*proc16)(const uint16_t*, int, const uint8_t*, int, uint16_t*) = mergeT;
+            return (MergeAAProc)proc16;
+        }
+        case SkMask::kLCD32_Format: {
+            void (*proc32)(const SkPMColor*, int, const uint8_t*, int, SkPMColor*) = mergeT;
+            return (MergeAAProc)proc32;
+        }
+        default:
+            SkDEBUGFAIL("unsupported");
+            return NULL;
+    }
+}
+
+static U8CPU bit2byte(int bitInAByte) {
+    SkASSERT(bitInAByte <= 0xFF);
+    // negation turns any non-zero into 0xFFFFFF??, so we just shift down
+    // some value >= 8 to get a full FF value
+    return -bitInAByte >> 8;
+}
+
+static void upscaleBW2A8(SkMask* dstMask, const SkMask& srcMask) {
+    SkASSERT(SkMask::kBW_Format == srcMask.fFormat);
+    SkASSERT(SkMask::kA8_Format == dstMask->fFormat);
+
+    const int width = srcMask.fBounds.width();
+    const int height = srcMask.fBounds.height();
+
+    const uint8_t* SK_RESTRICT src = (const uint8_t*)srcMask.fImage;
+    const size_t srcRB = srcMask.fRowBytes;
+    uint8_t* SK_RESTRICT dst = (uint8_t*)dstMask->fImage;
+    const size_t dstRB = dstMask->fRowBytes;
+
+    const int wholeBytes = width >> 3;
+    const int leftOverBits = width & 7;
+
+    for (int y = 0; y < height; ++y) {
+        uint8_t* SK_RESTRICT d = dst;
+        for (int i = 0; i < wholeBytes; ++i) {
+            int srcByte = src[i];
+            d[0] = bit2byte(srcByte & (1 << 7));
+            d[1] = bit2byte(srcByte & (1 << 6));
+            d[2] = bit2byte(srcByte & (1 << 5));
+            d[3] = bit2byte(srcByte & (1 << 4));
+            d[4] = bit2byte(srcByte & (1 << 3));
+            d[5] = bit2byte(srcByte & (1 << 2));
+            d[6] = bit2byte(srcByte & (1 << 1));
+            d[7] = bit2byte(srcByte & (1 << 0));
+            d += 8;
+        }
+        if (leftOverBits) {
+            int srcByte = src[wholeBytes];
+            for (int x = 0; x < leftOverBits; ++x) {
+                *d++ = bit2byte(srcByte & 0x80);
+                srcByte <<= 1;
+            }
+        }
+        src += srcRB;
+        dst += dstRB;
+    }
+}
+
+void SkAAClipBlitter::blitMask(const SkMask& origMask, const SkIRect& clip) {
+    SkASSERT(fAAClip->getBounds().contains(clip));
+
+    if (fAAClip->quickContains(clip)) {
+        fBlitter->blitMask(origMask, clip);
+        return;
+    }
+
+    const SkMask* mask = &origMask;
+
+    // if we're BW, we need to upscale to A8 (ugh)
+    SkMask  grayMask;
+    grayMask.fImage = NULL;
+    if (SkMask::kBW_Format == origMask.fFormat) {
+        grayMask.fFormat = SkMask::kA8_Format;
+        grayMask.fBounds = origMask.fBounds;
+        grayMask.fRowBytes = origMask.fBounds.width();
+        size_t size = grayMask.computeImageSize();
+        grayMask.fImage = (uint8_t*)fGrayMaskScratch.reset(size,
+                                               SkAutoMalloc::kReuse_OnShrink);
+
+        upscaleBW2A8(&grayMask, origMask);
+        mask = &grayMask;
+    }
+
+    this->ensureRunsAndAA();
+
+    // HACK -- we are devolving 3D into A8, need to copy the rest of the 3D
+    // data into a temp block to support it better (ugh)
+
+    const void* src = mask->getAddr(clip.fLeft, clip.fTop);
+    const size_t srcRB = mask->fRowBytes;
+    const int width = clip.width();
+    MergeAAProc mergeProc = find_merge_aa_proc(mask->fFormat);
+
+    SkMask rowMask;
+    rowMask.fFormat = SkMask::k3D_Format == mask->fFormat ? SkMask::kA8_Format : mask->fFormat;
+    rowMask.fBounds.fLeft = clip.fLeft;
+    rowMask.fBounds.fRight = clip.fRight;
+    rowMask.fRowBytes = mask->fRowBytes; // doesn't matter, since our height==1
+    rowMask.fImage = (uint8_t*)fScanlineScratch;
+
+    int y = clip.fTop;
+    const int stopY = y + clip.height();
+
+    do {
+        int localStopY;
+        const uint8_t* row = fAAClip->findRow(y, &localStopY);
+        // findRow returns last Y, not stop, so we add 1
+        localStopY = SkMin32(localStopY + 1, stopY);
+
+        int initialCount;
+        row = fAAClip->findX(row, clip.fLeft, &initialCount);
+        do {
+            mergeProc(src, width, row, initialCount, rowMask.fImage);
+            rowMask.fBounds.fTop = y;
+            rowMask.fBounds.fBottom = y + 1;
+            fBlitter->blitMask(rowMask, rowMask.fBounds);
+            src = (const void*)((const char*)src + srcRB);
+        } while (++y < localStopY);
+    } while (y < stopY);
+}
+
+const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
+    return NULL;
+}
+
diff --git a/legacy/src/core/SkAAClip.h b/legacy/src/core/SkAAClip.h
new file mode 100644
index 0000000..6036427
--- /dev/null
+++ b/legacy/src/core/SkAAClip.h
@@ -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.
+ */
+
+#ifndef SkAAClip_DEFINED
+#define SkAAClip_DEFINED
+
+#include "SkBlitter.h"
+#include "SkRegion.h"
+
+class SkAAClip {
+public:
+    SkAAClip();
+    SkAAClip(const SkAAClip&);
+    ~SkAAClip();
+
+    SkAAClip& operator=(const SkAAClip&);
+    friend bool operator==(const SkAAClip&, const SkAAClip&);
+    friend bool operator!=(const SkAAClip& a, const SkAAClip& b) {
+        return !(a == b);
+    }
+
+    void swap(SkAAClip&);
+
+    bool isEmpty() const { return NULL == fRunHead; }
+    const SkIRect& getBounds() const { return fBounds; }
+
+    bool setEmpty();
+    bool setRect(const SkIRect&);
+    bool setRect(const SkRect&, bool doAA = true);
+    bool setPath(const SkPath&, const SkRegion* clip = NULL, bool doAA = true);
+    bool setRegion(const SkRegion&);
+    bool set(const SkAAClip&);
+
+    bool op(const SkAAClip&, const SkAAClip&, SkRegion::Op);
+
+    // Helpers for op()
+    bool op(const SkIRect&, SkRegion::Op);
+    bool op(const SkRect&, SkRegion::Op, bool doAA);
+    bool op(const SkAAClip&, SkRegion::Op);
+
+    bool translate(int dx, int dy, SkAAClip* dst) const;
+    bool translate(int dx, int dy) {
+        return this->translate(dx, dy, this);
+    }
+
+    /**
+     *  Allocates a mask the size of the aaclip, and expands its data into
+     *  the mask, using kA8_Format
+     */
+    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;
+
+    class Iter;
+    struct RunHead;
+    struct YOffset;
+    class Builder;
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    SkIRect  fBounds;
+    RunHead* fRunHead;
+
+    void freeRuns();
+    bool trimBounds();
+    bool trimTopBottom();
+    bool trimLeftRight();
+
+    friend class Builder;
+    class BuilderBlitter;
+    friend class BuilderBlitter;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAAClipBlitter : public SkBlitter {
+public:
+    SkAAClipBlitter() : fScanlineScratch(NULL) {}
+    virtual ~SkAAClipBlitter();
+
+    void init(SkBlitter* blitter, const SkAAClip* aaclip) {
+        SkASSERT(aaclip && !aaclip->isEmpty());
+        fBlitter = blitter;
+        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;
+    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;
+    
+private:
+    SkBlitter*      fBlitter;
+    const SkAAClip* fAAClip;
+    SkIRect         fAAClipBounds;
+
+    // point into fScanlineScratch
+    int16_t*        fRuns;
+    SkAlpha*        fAA;
+
+    enum {
+        kSize = 32 * 32
+    };
+    SkAutoSMalloc<kSize> fGrayMaskScratch;  // used for blitMask
+    void* fScanlineScratch;  // enough for a mask at 32bit, or runs+aa
+
+    void ensureRunsAndAA();
+};
+
+#endif
diff --git a/legacy/src/core/SkAdvancedTypefaceMetrics.cpp b/legacy/src/core/SkAdvancedTypefaceMetrics.cpp
new file mode 100644
index 0000000..8af70fb
--- /dev/null
+++ b/legacy/src/core/SkAdvancedTypefaceMetrics.cpp
@@ -0,0 +1,299 @@
+
+/*
+ * 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 "SkAdvancedTypefaceMetrics.h"
+#include "SkTypes.h"
+
+#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#endif
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+namespace skia_advanced_typeface_metrics_utils {
+
+const int16_t kInvalidAdvance = SK_MinS16;
+const int16_t kDontCareAdvance = SK_MinS16 + 1;
+
+template <typename Data>
+void stripUninterestingTrailingAdvancesFromRange(
+                                                 SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range) {
+    SkASSERT(false);
+}
+
+template <>
+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 &&
+            range->fAdvance[i] != 0) {
+            range->fEndId = range->fStartId + i;
+            break;
+        }
+    }
+}
+
+template <typename Data>
+void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
+                int startId) {
+    range->fStartId = startId;
+    range->fAdvance.setCount(0);
+}
+
+template <typename Data>
+SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange(
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot,
+        int startId) {
+    nextSlot->reset(new SkAdvancedTypefaceMetrics::AdvanceMetric<Data>);
+    resetRange(nextSlot->get(), startId);
+    return nextSlot->get();
+}
+
+template <typename Data>
+void zeroWildcardsInRange(
+                          SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range) {
+    SkASSERT(false);
+}
+
+template <>
+void zeroWildcardsInRange<int16_t>(
+                                   SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* range) {
+    SkASSERT(range);
+    if (range->fType != SkAdvancedTypefaceMetrics::WidthRange::kRange) {
+        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) {
+            range->fAdvance[i] = 0;
+        }
+    }
+}
+    
+template <typename Data>
+void finishRange(
+        SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
+        int endId,
+        typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType
+                type) {
+    range->fEndId = endId;
+    range->fType = type;
+    stripUninterestingTrailingAdvancesFromRange(range);
+    int newLength;
+    if (type == SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange) {
+        newLength = range->fEndId - range->fStartId + 1;
+    } else {
+        if (range->fEndId == range->fStartId) {
+            range->fType =
+                SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange;
+        }
+        newLength = 1;
+    }
+    SkASSERT(range->fAdvance.count() >= newLength);
+    range->fAdvance.setCount(newLength);
+    zeroWildcardsInRange(range);
+}
+
+template <typename Data, typename FontHandle>
+SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
+        FontHandle fontHandle,
+        int num_glyphs,
+        const uint32_t* subsetGlyphIDs,
+        uint32_t subsetGlyphIDsLength,
+        bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)) {
+    // Assuming that on average, the ASCII representation of an advance plus
+    // a space is 8 characters and the ASCII representation of a glyph id is 3
+    // characters, then the following cut offs for using different range types
+    // apply:
+    // The cost of stopping and starting the range is 7 characers
+    //  a. Removing 4 0's or don't care's is a win
+    // The cost of stopping and starting the range plus a run is 22
+    // characters
+    //  b. Removing 3 repeating advances is a win
+    //  c. Removing 2 repeating advances and 3 don't cares is a win
+    // When not currently in a range the cost of a run over a range is 16
+    // characaters, so:
+    //  d. Removing a leading 0/don't cares is a win because it is omitted
+    //  e. Removing 2 repeating advances is a win
+
+    SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result;
+    SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange;
+    SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* prevRange = NULL;
+    Data lastAdvance = kInvalidAdvance;
+    int repeatedAdvances = 0;
+    int wildCardsInRun = 0;
+    int leadingWildCards = 0;
+    int trailingWildCards = 0;
+    uint32_t subsetIndex = 0;
+
+    // Limit the loop count to glyph id ranges provided.
+    int firstIndex = 0;
+    int lastIndex = num_glyphs;
+    if (subsetGlyphIDs) {
+        firstIndex = static_cast<int>(subsetGlyphIDs[0]);
+        lastIndex =
+                static_cast<int>(subsetGlyphIDs[subsetGlyphIDsLength - 1]) + 1;
+    }
+    curRange = appendRange(&result, firstIndex);
+
+    for (int gId = firstIndex; gId <= lastIndex; gId++) {
+        Data advance = kInvalidAdvance;
+        if (gId < lastIndex) {
+            // Get glyph id only when subset is NULL, or the id is in subset.
+            if (!subsetGlyphIDs ||
+                (subsetIndex < subsetGlyphIDsLength &&
+                 static_cast<uint32_t>(gId) == subsetGlyphIDs[subsetIndex])) {
+                SkAssertResult(getAdvance(fontHandle, gId, &advance));
+                ++subsetIndex;
+            } else {
+                advance = kDontCareAdvance;
+            }
+        }
+        if (advance == lastAdvance) {
+            repeatedAdvances++;
+            trailingWildCards = 0;
+        } else if (advance == kDontCareAdvance) {
+            wildCardsInRun++;
+            trailingWildCards++;
+        } else if (curRange->fAdvance.count() ==
+                   repeatedAdvances + 1 + wildCardsInRun) {  // All in run.
+            if (lastAdvance == 0) {
+                resetRange(curRange, gId);
+                trailingWildCards = 0;
+            } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) {
+                finishRange(curRange, gId - 1,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
+                prevRange = curRange;
+                curRange = appendRange(&curRange->fNext, gId);
+                trailingWildCards = 0;
+            }
+            repeatedAdvances = 0;
+            wildCardsInRun = trailingWildCards;
+            leadingWildCards = trailingWildCards;
+            trailingWildCards = 0;
+        } else {
+            if (lastAdvance == 0 &&
+                    repeatedAdvances + 1 + wildCardsInRun >= 4) {
+                finishRange(curRange,
+                            gId - repeatedAdvances - wildCardsInRun - 2,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
+                prevRange = curRange;
+                curRange = appendRange(&curRange->fNext, gId);
+                trailingWildCards = 0;
+            } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) {
+                finishRange(curRange,
+                            gId - trailingWildCards - 1,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
+                prevRange = curRange;
+                curRange = appendRange(&curRange->fNext, gId);
+                trailingWildCards = 0;
+            } else if (lastAdvance != 0 &&
+                       (repeatedAdvances + 1 >= 3 ||
+                        (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) {
+                finishRange(curRange,
+                            gId - repeatedAdvances - wildCardsInRun - 2,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
+                curRange =
+                    appendRange(&curRange->fNext,
+                                gId - repeatedAdvances - wildCardsInRun - 1);
+                curRange->fAdvance.append(1, &lastAdvance);
+                finishRange(curRange, gId - 1,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
+                prevRange = curRange;
+                curRange = appendRange(&curRange->fNext, gId);
+                trailingWildCards = 0;
+            }
+            repeatedAdvances = 0;
+            wildCardsInRun = trailingWildCards;
+            leadingWildCards = trailingWildCards;
+            trailingWildCards = 0;
+        }
+        curRange->fAdvance.append(1, &advance);
+        if (advance != kDontCareAdvance) {
+            lastAdvance = advance;
+        }
+    }
+    if (curRange->fStartId == lastIndex) {
+        SkASSERT(prevRange);
+        SkASSERT(prevRange->fNext->fStartId == lastIndex);
+        prevRange->fNext.reset();
+    } else {
+        finishRange(curRange, lastIndex - 1,
+                    SkAdvancedTypefaceMetrics::WidthRange::kRange);
+    }
+    return result.release();
+}
+
+// Make AdvanceMetric template functions available for linking with typename
+// WidthRange and VerticalAdvanceRange.
+#if defined(SK_BUILD_FOR_WIN)
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        HDC hdc,
+        int num_glyphs,
+        const uint32_t* subsetGlyphIDs,
+        uint32_t subsetGlyphIDsLength,
+        bool (*getAdvance)(HDC hdc, int gId, int16_t* data));
+#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        FT_Face face,
+        int num_glyphs,
+        const uint32_t* subsetGlyphIDs,
+        uint32_t subsetGlyphIDsLength,
+        bool (*getAdvance)(FT_Face face, int gId, int16_t* data));
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        CTFontRef ctFont,
+        int num_glyphs,
+        const uint32_t* subsetGlyphIDs,
+        uint32_t subsetGlyphIDsLength,
+        bool (*getAdvance)(CTFontRef ctFont, int gId, int16_t* data));
+#endif
+template void resetRange(
+        SkAdvancedTypefaceMetrics::WidthRange* range,
+        int startId);
+template SkAdvancedTypefaceMetrics::WidthRange* appendRange(
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::WidthRange >* nextSlot,
+        int startId);
+template void finishRange<int16_t>(
+        SkAdvancedTypefaceMetrics::WidthRange* range,
+        int endId,
+        SkAdvancedTypefaceMetrics::WidthRange::MetricType type);
+
+template void resetRange(
+        SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range,
+        int startId);
+template SkAdvancedTypefaceMetrics::VerticalAdvanceRange* appendRange(
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::VerticalAdvanceRange >*
+            nextSlot,
+        int startId);
+template void finishRange<SkAdvancedTypefaceMetrics::VerticalMetric>(
+        SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range,
+        int endId,
+        SkAdvancedTypefaceMetrics::VerticalAdvanceRange::MetricType type);
+
+} // namespace skia_advanced_typeface_metrics_utils
diff --git a/legacy/src/core/SkAlphaRuns.cpp b/legacy/src/core/SkAlphaRuns.cpp
new file mode 100644
index 0000000..116d132
--- /dev/null
+++ b/legacy/src/core/SkAlphaRuns.cpp
@@ -0,0 +1,179 @@
+
+/*
+ * 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 "SkAntiRun.h"
+#include "SkUtils.h"
+
+void SkAlphaRuns::reset(int width) {
+    SkASSERT(width > 0);
+
+#ifdef SK_DEBUG
+    sk_memset16((uint16_t*)fRuns, (uint16_t)(-42), width);
+#endif
+    fRuns[0] = SkToS16(width);
+    fRuns[width] = 0;
+    fAlpha[0] = 0;
+
+    SkDEBUGCODE(fWidth = width;)
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkAlphaRuns::Break(int16_t runs[], uint8_t alpha[], int x, int count) {
+    SkASSERT(count > 0 && x >= 0);
+
+//  SkAlphaRuns::BreakAt(runs, alpha, x);
+//  SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count);
+
+    int16_t* next_runs = runs + x;
+    uint8_t*  next_alpha = alpha + x;
+
+    while (x > 0) {
+        int n = runs[0];
+        SkASSERT(n > 0);
+
+        if (x < n) {
+            alpha[x] = alpha[0];
+            runs[0] = SkToS16(x);
+            runs[x] = SkToS16(n - x);
+            break;
+        }
+        runs += n;
+        alpha += n;
+        x -= n;
+    }
+
+    runs = next_runs;
+    alpha = next_alpha;
+    x = count;
+
+    for (;;) {
+        int n = runs[0];
+        SkASSERT(n > 0);
+
+        if (x < n) {
+            alpha[x] = alpha[0];
+            runs[0] = SkToS16(x);
+            runs[x] = SkToS16(n - x);
+            break;
+        }
+        x -= n;
+        if (x <= 0) {
+            break;
+        }
+        runs += n;
+        alpha += n;
+    }
+}
+
+int SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
+                     U8CPU maxValue, int offsetX) {
+    SkASSERT(middleCount >= 0);
+    SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth);
+
+    SkASSERT(fRuns[offsetX] >= 0);
+
+    int16_t*    runs = fRuns + offsetX;
+    uint8_t*    alpha = fAlpha + offsetX;
+    uint8_t*    lastAlpha = alpha;
+    x -= offsetX;
+
+    if (startAlpha) {
+        SkAlphaRuns::Break(runs, alpha, x, 1);
+        /*  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 (crud).
+        */
+        unsigned tmp = alpha[x] + startAlpha;
+        SkASSERT(tmp <= 256);
+        alpha[x] = SkToU8(tmp - (tmp >> 8));    // was (tmp >> 7), but that seems wrong if we're trying to catch 256
+
+        runs += x + 1;
+        alpha += x + 1;
+        x = 0;
+        lastAlpha += x; // we don't want the +1
+        SkDEBUGCODE(this->validate();)
+    }
+
+    if (middleCount) {
+        SkAlphaRuns::Break(runs, alpha, x, middleCount);
+        alpha += x;
+        runs += x;
+        x = 0;
+        do {
+            alpha[0] = SkToU8(alpha[0] + maxValue);
+            int n = runs[0];
+            SkASSERT(n <= middleCount);
+            alpha += n;
+            runs += n;
+            middleCount -= n;
+        } while (middleCount > 0);
+        SkDEBUGCODE(this->validate();)
+        lastAlpha = alpha;
+    }
+
+    if (stopAlpha) {
+        SkAlphaRuns::Break(runs, alpha, x, 1);
+        alpha += x;
+        alpha[0] = SkToU8(alpha[0] + stopAlpha);
+        SkDEBUGCODE(this->validate();)
+        lastAlpha = alpha;
+    }
+
+    return lastAlpha - fAlpha;  // new offsetX
+}
+
+#ifdef SK_DEBUG
+    void SkAlphaRuns::assertValid(int y, int maxStep) const {
+        int max = (y + 1) * maxStep - (y == maxStep - 1);
+
+        const int16_t* runs = fRuns;
+        const uint8_t*   alpha = fAlpha;
+
+        while (*runs) {
+            SkASSERT(*alpha <= max);
+            alpha += *runs;
+            runs += *runs;
+        }
+    }
+
+    void SkAlphaRuns::dump() const {
+        const int16_t* runs = fRuns;
+        const uint8_t* alpha = fAlpha;
+
+        SkDebugf("Runs");
+        while (*runs) {
+            int n = *runs;
+
+            SkDebugf(" %02x", *alpha);
+            if (n > 1) {
+                SkDebugf(",%d", n);
+            }
+            alpha += n;
+            runs += n;
+        }
+        SkDebugf("\n");
+    }
+
+    void SkAlphaRuns::validate() const {
+        SkASSERT(fWidth > 0);
+
+        int         count = 0;
+        const int16_t*  runs = fRuns;
+
+        while (*runs) {
+            SkASSERT(*runs > 0);
+            count += *runs;
+            SkASSERT(count <= fWidth);
+            runs += *runs;
+        }
+        SkASSERT(count == fWidth);
+    }
+#endif
+
diff --git a/legacy/src/core/SkAntiRun.h b/legacy/src/core/SkAntiRun.h
new file mode 100644
index 0000000..56b5ee5
--- /dev/null
+++ b/legacy/src/core/SkAntiRun.h
@@ -0,0 +1,93 @@
+
+/*
+ * 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 SkAntiRun_DEFINED
+#define SkAntiRun_DEFINED
+
+#include "SkBlitter.h"
+
+/** Sparse array of run-length-encoded alpha (supersampling coverage) values.
+    Sparseness allows us to independently compose several paths into the
+    same SkAlphaRuns buffer.
+*/
+
+class SkAlphaRuns {
+public:
+    int16_t*    fRuns;
+    uint8_t*     fAlpha;
+
+    /// Returns true if the scanline contains only a single run,
+    /// of alpha value 0.
+    bool empty() const {
+        SkASSERT(fRuns[0] > 0);
+        return fAlpha[0] == 0 && fRuns[fRuns[0]] == 0;
+    }
+
+    /// Reinitialize for a new scanline.
+    void    reset(int width);
+    
+    /**
+     *  Insert into the buffer a run starting at (x-offsetX):
+     *      if startAlpha > 0
+     *          one pixel with value += startAlpha,
+     *              max 255
+     *      if middleCount > 0
+     *          middleCount pixels with value += maxValue
+     *      if stopAlpha > 0
+     *          one pixel with value += stopAlpha
+     *  Returns the offsetX value that should be passed on the next call,
+     *  assuming we're on the same scanline. If the caller is switching
+     *  scanlines, then offsetX should be 0 when this is called.
+     */
+    int add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
+            U8CPU maxValue, int offsetX);
+
+    SkDEBUGCODE(void assertValid(int y, int maxStep) const;)
+    SkDEBUGCODE(void dump() const;)
+
+    /**
+     * Break the runs in the buffer at offsets x and x+count, properly
+     * updating the runs to the right and left.
+     *   i.e. from the state AAAABBBB, run-length encoded as A4B4,
+     *   Break(..., 2, 5) would produce AAAABBBB rle as A2A2B3B1.
+     * Allows add() to sum another run to some of the new sub-runs.
+     *   i.e. adding ..CCCCC. would produce AADDEEEB, rle as A2D2E3B1.
+     */
+    static void Break(int16_t runs[], uint8_t alpha[], int x, int count);
+
+    /**
+     * Cut (at offset x in the buffer) a run into two shorter runs with
+     * matching alpha values.
+     * Used by the RectClipBlitter to trim a RLE encoding to match the
+     * clipping rectangle.
+     */
+    static void BreakAt(int16_t runs[], uint8_t alpha[], int x) {
+        while (x > 0) {
+            int n = runs[0];
+            SkASSERT(n > 0);
+
+            if (x < n) {
+                alpha[x] = alpha[0];
+                runs[0] = SkToS16(x);
+                runs[x] = SkToS16(n - x);
+                break;
+            }
+            runs += n;
+            alpha += n;
+            x -= n;
+        }
+    }
+
+private:
+    SkDEBUGCODE(int fWidth;)
+    SkDEBUGCODE(void validate() const;)
+};
+
+#endif
+
diff --git a/legacy/src/core/SkBitmap.cpp b/legacy/src/core/SkBitmap.cpp
new file mode 100644
index 0000000..0b98513
--- /dev/null
+++ b/legacy/src/core/SkBitmap.cpp
@@ -0,0 +1,1544 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkFlattenable.h"
+#include "SkMallocPixelRef.h"
+#include "SkMask.h"
+#include "SkPixelRef.h"
+#include "SkThread.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkPackBits.h"
+#include <new>
+
+extern int32_t SkNextPixelRefGenerationID();
+
+static bool isPos32Bits(const Sk64& value) {
+    return !value.isNeg() && value.is32();
+}
+
+struct MipLevel {
+    void*       fPixels;
+    uint32_t    fRowBytes;
+    uint32_t    fWidth, fHeight;
+};
+
+struct SkBitmap::MipMap : SkNoncopyable {
+    int32_t fRefCnt;
+    int     fLevelCount;
+//  MipLevel    fLevel[fLevelCount];
+//  Pixels[]
+
+    static MipMap* Alloc(int levelCount, size_t pixelSize) {
+        if (levelCount < 0) {
+            return NULL;
+        }
+        Sk64 size;
+        size.setMul(levelCount + 1, sizeof(MipLevel));
+        size.add(sizeof(MipMap));
+        size.add(pixelSize);
+        if (!isPos32Bits(size)) {
+            return NULL;
+        }
+        MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
+        mm->fRefCnt = 1;
+        mm->fLevelCount = levelCount;
+        return mm;
+    }
+
+    const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
+    MipLevel* levels() { return (MipLevel*)(this + 1); }
+
+    const void* pixels() const { return levels() + fLevelCount; }
+    void* pixels() { return levels() + fLevelCount; }
+
+    void ref() {
+        if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
+            sk_throw();
+        }
+    }
+    void unref() {
+        SkASSERT(fRefCnt > 0);
+        if (sk_atomic_dec(&fRefCnt) == 1) {
+            sk_free(this);
+        }
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmap::SkBitmap() {
+    sk_bzero(this, sizeof(*this));
+}
+
+SkBitmap::SkBitmap(const SkBitmap& src) {
+    SkDEBUGCODE(src.validate();)
+    sk_bzero(this, sizeof(*this));
+    *this = src;
+    SkDEBUGCODE(this->validate();)
+}
+
+SkBitmap::~SkBitmap() {
+    SkDEBUGCODE(this->validate();)
+    this->freePixels();
+}
+
+SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
+    if (this != &src) {
+        this->freePixels();
+        memcpy(this, &src, sizeof(src));
+
+        // inc src reference counts
+        SkSafeRef(src.fPixelRef);
+        SkSafeRef(src.fMipMap);
+
+        // we reset our locks if we get blown away
+        fPixelLockCount = 0;
+
+        /*  The src could be in 3 states
+            1. no pixelref, in which case we just copy/ref the pixels/ctable
+            2. unlocked pixelref, pixels/ctable should be null
+            3. locked pixelref, we should lock the ref again ourselves
+        */
+        if (NULL == fPixelRef) {
+            // leave fPixels as it is
+            SkSafeRef(fColorTable); // ref the user's ctable if present
+        } else {    // we have a pixelref, so pixels/ctable reflect it
+            // ignore the values from the memcpy
+            fPixels = NULL;
+            fColorTable = NULL;
+            // Note that what to for genID is somewhat arbitrary. We have no
+            // way to track changes to raw pixels across multiple SkBitmaps.
+            // Would benefit from an SkRawPixelRef type created by
+            // setPixels.
+            // Just leave the memcpy'ed one but they'll get out of sync
+            // as soon either is modified.
+        }
+    }
+
+    SkDEBUGCODE(this->validate();)
+    return *this;
+}
+
+void SkBitmap::swap(SkBitmap& other) {
+    SkTSwap(fColorTable, other.fColorTable);
+    SkTSwap(fPixelRef, other.fPixelRef);
+    SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
+    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);
+    SkTSwap(fConfig, other.fConfig);
+    SkTSwap(fFlags, other.fFlags);
+    SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
+
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::reset() {
+    this->freePixels();
+    sk_bzero(this, sizeof(*this));
+}
+
+int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
+    int bpp;
+    switch (config) {
+        case kNo_Config:
+        case kA1_Config:
+            bpp = 0;   // not applicable
+            break;
+        case kRLE_Index8_Config:
+        case kA8_Config:
+        case kIndex8_Config:
+            bpp = 1;
+            break;
+        case kRGB_565_Config:
+        case kARGB_4444_Config:
+            bpp = 2;
+            break;
+        case kARGB_8888_Config:
+            bpp = 4;
+            break;
+        default:
+            SkDEBUGFAIL("unknown config");
+            bpp = 0;   // error
+            break;
+    }
+    return bpp;
+}
+
+int SkBitmap::ComputeRowBytes(Config c, int width) {
+    if (width < 0) {
+        return 0;
+    }
+
+    Sk64 rowBytes;
+    rowBytes.setZero();
+
+    switch (c) {
+        case kNo_Config:
+        case kRLE_Index8_Config:
+            break;
+        case kA1_Config:
+            rowBytes.set(width);
+            rowBytes.add(7);
+            rowBytes.shiftRight(3);
+            break;
+        case kA8_Config:
+        case kIndex8_Config:
+            rowBytes.set(width);
+            break;
+        case kRGB_565_Config:
+        case kARGB_4444_Config:
+            rowBytes.set(width);
+            rowBytes.shiftLeft(1);
+            break;
+        case kARGB_8888_Config:
+            rowBytes.set(width);
+            rowBytes.shiftLeft(2);
+            break;
+        default:
+            SkDEBUGFAIL("unknown config");
+            break;
+    }
+    return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
+}
+
+Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
+    Sk64 size;
+    size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
+    return size;
+}
+
+size_t SkBitmap::ComputeSize(Config c, int width, int height) {
+    Sk64 size = SkBitmap::ComputeSize64(c, width, height);
+    return isPos32Bits(size) ? size.get32() : 0;
+}
+
+Sk64 SkBitmap::ComputeSafeSize64(Config config,
+                                 uint32_t width,
+                                 uint32_t height,
+                                 uint32_t rowBytes) {
+    Sk64 safeSize;
+    safeSize.setZero();
+    if (height > 0) {
+        safeSize.set(ComputeRowBytes(config, width));
+        Sk64 sizeAllButLastRow;
+        sizeAllButLastRow.setMul(height - 1, rowBytes);
+        safeSize.add(sizeAllButLastRow);
+    }
+    SkASSERT(!safeSize.isNeg());
+    return safeSize;
+}
+
+size_t SkBitmap::ComputeSafeSize(Config config,
+                                 uint32_t width,
+                                 uint32_t height,
+                                 uint32_t rowBytes) {
+    Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
+    return (safeSize.is32() ? safeSize.get32() : 0);
+}
+
+void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
+    this->freePixels();
+
+    if ((width | height | rowBytes) < 0) {
+        goto err;
+    }
+
+    if (rowBytes == 0) {
+        rowBytes = SkBitmap::ComputeRowBytes(c, width);
+        if (0 == rowBytes && kNo_Config != c) {
+            goto err;
+        }
+    }
+
+    fConfig     = SkToU8(c);
+    fWidth      = width;
+    fHeight     = height;
+    fRowBytes   = rowBytes;
+
+    fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
+
+    SkDEBUGCODE(this->validate();)
+    return;
+
+    // if we got here, we had an error, so we reset the bitmap to empty
+err:
+    this->reset();
+}
+
+void SkBitmap::updatePixelsFromRef() const {
+    if (NULL != fPixelRef) {
+        if (fPixelLockCount > 0) {
+            SkASSERT(fPixelRef->isLocked());
+
+            void* p = fPixelRef->pixels();
+            if (NULL != p) {
+                p = (char*)p + fPixelRefOffset;
+            }
+            fPixels = p;
+            SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
+        } else {
+            SkASSERT(0 == fPixelLockCount);
+            fPixels = NULL;
+            if (fColorTable) {
+                fColorTable->unref();
+                fColorTable = NULL;
+            }
+        }
+    }
+}
+
+SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
+    // do this first, we that we never have a non-zero offset with a null ref
+    if (NULL == pr) {
+        offset = 0;
+    }
+
+    if (fPixelRef != pr || fPixelRefOffset != offset) {
+        if (fPixelRef != pr) {
+            this->freePixels();
+            SkASSERT(NULL == fPixelRef);
+
+            SkSafeRef(pr);
+            fPixelRef = pr;
+        }
+        fPixelRefOffset = offset;
+        this->updatePixelsFromRef();
+    }
+
+    SkDEBUGCODE(this->validate();)
+    return pr;
+}
+
+void SkBitmap::lockPixels() const {
+    if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
+        fPixelRef->lockPixels();
+        this->updatePixelsFromRef();
+    }
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::unlockPixels() const {
+    SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
+
+    if (NULL != fPixelRef && 0 == --fPixelLockCount) {
+        fPixelRef->unlockPixels();
+        this->updatePixelsFromRef();
+    }
+    SkDEBUGCODE(this->validate();)
+}
+
+bool SkBitmap::lockPixelsAreWritable() const {
+    if (fPixelRef) {
+        return fPixelRef->lockPixelsAreWritable();
+    } else {
+        return fPixels != NULL;
+    }
+}
+
+void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
+    this->freePixels();
+    fPixels = p;
+    SkRefCnt_SafeAssign(fColorTable, ctable);
+
+    SkDEBUGCODE(this->validate();)
+}
+
+bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
+    HeapAllocator stdalloc;
+
+    if (NULL == allocator) {
+        allocator = &stdalloc;
+    }
+    return allocator->allocPixelRef(this, ctable);
+}
+
+void SkBitmap::freePixels() {
+    // if we're gonna free the pixels, we certainly need to free the mipmap
+    this->freeMipMap();
+
+    if (fColorTable) {
+        fColorTable->unref();
+        fColorTable = NULL;
+    }
+
+    if (NULL != fPixelRef) {
+        if (fPixelLockCount > 0) {
+            fPixelRef->unlockPixels();
+        }
+        fPixelRef->unref();
+        fPixelRef = NULL;
+        fPixelRefOffset = 0;
+    }
+    fPixelLockCount = 0;
+    fPixels = NULL;
+}
+
+void SkBitmap::freeMipMap() {
+    if (fMipMap) {
+        fMipMap->unref();
+        fMipMap = NULL;
+    }
+}
+
+uint32_t SkBitmap::getGenerationID() const {
+    if (fPixelRef) {
+        return fPixelRef->getGenerationID();
+    } else {
+        SkASSERT(fPixels || !fRawPixelGenerationID);
+        if (fPixels && !fRawPixelGenerationID) {
+            fRawPixelGenerationID = SkNextPixelRefGenerationID();
+        }
+        return fRawPixelGenerationID;
+    }
+}
+
+void SkBitmap::notifyPixelsChanged() const {
+    SkASSERT(!this->isImmutable());
+    if (fPixelRef) {
+        fPixelRef->notifyPixelsChanged();
+    } else {
+        fRawPixelGenerationID = 0; // will grab next ID in getGenerationID
+    }
+}
+
+SkGpuTexture* SkBitmap::getTexture() const {
+    return fPixelRef ? fPixelRef->getTexture() : NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** We explicitly use the same allocator for our pixels that SkMask does,
+ so that we can freely assign memory allocated by one class to the other.
+ */
+bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
+                                            SkColorTable* ctable) {
+    Sk64 size = dst->getSize64();
+    if (size.isNeg() || !size.is32()) {
+        return false;
+    }
+
+    void* addr = sk_malloc_flags(size.get32(), 0);  // returns NULL on failure
+    if (NULL == addr) {
+        return false;
+    }
+
+    dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
+    // since we're already allocated, we lockPixels right away
+    dst->lockPixels();
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkBitmap::getSafeSize() const {
+    // This is intended to be a size_t version of ComputeSafeSize64(), just
+    // faster. The computation is meant to be identical.
+    return (fHeight ? ((fHeight - 1) * fRowBytes) +
+            ComputeRowBytes(getConfig(), fWidth): 0);
+}
+
+Sk64 SkBitmap::getSafeSize64() const {
+    return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
+}
+
+bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, 
+                            int dstRowBytes, bool preserveDstPad) const {
+
+    if (dstRowBytes == -1)
+        dstRowBytes = fRowBytes;
+    SkASSERT(dstRowBytes >= 0);
+
+    if (getConfig() == kRLE_Index8_Config ||
+        dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
+        dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
+        return false;
+
+    if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
+        size_t safeSize = getSafeSize();
+        if (safeSize > dstSize || safeSize == 0)
+            return false;
+        else {
+            SkAutoLockPixels lock(*this);
+            // This implementation will write bytes beyond the end of each row,
+            // excluding the last row, if the bitmap's stride is greater than
+            // strictly required by the current config.
+            memcpy(dst, getPixels(), safeSize);
+
+            return true;
+        }
+    } else {
+        // If destination has different stride than us, then copy line by line.
+        if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
+            dstSize)
+            return false;
+        else {
+            // Just copy what we need on each line.
+            uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
+            SkAutoLockPixels lock(*this);
+            const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
+            uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
+            for (uint32_t row = 0; row < fHeight;
+                 row++, srcP += fRowBytes, dstP += dstRowBytes) {
+                memcpy(dstP, srcP, rowBytes);
+            }
+
+            return true;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkBitmap::isImmutable() const { 
+    return fPixelRef ? fPixelRef->isImmutable() :
+        fFlags & kImageIsImmutable_Flag; 
+}
+
+void SkBitmap::setImmutable() {
+    if (fPixelRef) {
+        fPixelRef->setImmutable();
+    } else {
+        fFlags |= kImageIsImmutable_Flag;
+    }
+}
+
+bool SkBitmap::isOpaque() const {
+    switch (fConfig) {
+        case kNo_Config:
+            return true;
+
+        case kA1_Config:
+        case kA8_Config:
+        case kARGB_4444_Config:
+        case kARGB_8888_Config:
+            return (fFlags & kImageIsOpaque_Flag) != 0;
+
+        case kIndex8_Config:
+        case kRLE_Index8_Config: {
+                uint32_t flags = 0;
+
+                this->lockPixels();
+                // if lockPixels failed, we may not have a ctable ptr
+                if (fColorTable) {
+                    flags = fColorTable->getFlags();
+                }
+                this->unlockPixels();
+
+                return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
+            }
+
+        case kRGB_565_Config:
+            return true;
+
+        default:
+            SkDEBUGFAIL("unknown bitmap config pased to isOpaque");
+            return false;
+    }
+}
+
+void SkBitmap::setIsOpaque(bool isOpaque) {
+    /*  we record this regardless of fConfig, though it is ignored in
+        isOpaque() for configs that can't support per-pixel alpha.
+    */
+    if (isOpaque) {
+        fFlags |= kImageIsOpaque_Flag;
+    } else {
+        fFlags &= ~kImageIsOpaque_Flag;
+    }
+}
+
+bool SkBitmap::isVolatile() const {
+    return (fFlags & kImageIsVolatile_Flag) != 0;
+}
+
+void SkBitmap::setIsVolatile(bool isVolatile) {
+    if (isVolatile) {
+        fFlags |= kImageIsVolatile_Flag;
+    } else {
+        fFlags &= ~kImageIsVolatile_Flag;
+    }
+}
+
+void* SkBitmap::getAddr(int x, int y) const {
+    SkASSERT((unsigned)x < (unsigned)this->width());
+    SkASSERT((unsigned)y < (unsigned)this->height());
+
+    char* base = (char*)this->getPixels();
+    if (base) {
+        base += y * this->rowBytes();
+        switch (this->config()) {
+            case SkBitmap::kARGB_8888_Config:
+                base += x << 2;
+                break;
+            case SkBitmap::kARGB_4444_Config:
+            case SkBitmap::kRGB_565_Config:
+                base += x << 1;
+                break;
+            case SkBitmap::kA8_Config:
+            case SkBitmap::kIndex8_Config:
+                base += x;
+                break;
+            case SkBitmap::kA1_Config:
+                base += x >> 3;
+                break;
+            case kRLE_Index8_Config:
+                SkDEBUGFAIL("Can't return addr for kRLE_Index8_Config");
+                base = NULL;
+                break;
+            default:
+                SkDEBUGFAIL("Can't return addr for config");
+                base = NULL;
+                break;
+        }
+    }
+    return base;
+}
+
+SkColor SkBitmap::getColor(int x, int y) const {
+    SkASSERT((unsigned)x < (unsigned)this->width());
+    SkASSERT((unsigned)y < (unsigned)this->height());
+
+    switch (this->config()) {
+        case SkBitmap::kA1_Config: {
+            uint8_t* addr = this->getAddr1(x, y);
+            uint8_t mask = 1 << (7  - (x % 8));
+            if (addr[0] & mask) {
+                return SK_ColorBLACK;
+            } else {
+                return 0;
+            }
+        }
+        case SkBitmap::kA8_Config: {
+            uint8_t* addr = this->getAddr8(x, y);
+            return SkColorSetA(0, addr[0]);
+        }
+        case SkBitmap::kIndex8_Config: {
+            SkPMColor c = this->getIndex8Color(x, y);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        case SkBitmap::kRGB_565_Config: {
+            uint16_t* addr = this->getAddr16(x, y);
+            return SkPixel16ToColor(addr[0]);
+        }
+        case SkBitmap::kARGB_4444_Config: {
+            uint16_t* addr = this->getAddr16(x, y);
+            SkPMColor c = SkPixel4444ToPixel32(addr[0]);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        case SkBitmap::kARGB_8888_Config: {
+            uint32_t* addr = this->getAddr32(x, y);
+            return SkUnPreMultiply::PMColorToColor(addr[0]);
+        }
+        case kRLE_Index8_Config: {
+            uint8_t dst;
+            const SkBitmap::RLEPixels* rle =
+                (const SkBitmap::RLEPixels*)this->getPixels();
+            SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
+            return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
+        }
+        case kNo_Config:
+        case kConfigCount:
+            SkASSERT(false);
+            return 0;
+    }
+    SkASSERT(false);  // Not reached.
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (0 == fWidth || 0 == fHeight ||
+            kNo_Config == fConfig || kIndex8_Config == fConfig) {
+        return;
+    }
+
+    SkAutoLockPixels alp(*this);
+    // perform this check after the lock call
+    if (!this->readyToDraw()) {
+        return;
+    }
+
+    int height = fHeight;
+    const int width = fWidth;
+    const int rowBytes = fRowBytes;
+
+    // make rgb premultiplied
+    if (255 != a) {
+        r = SkAlphaMul(r, a);
+        g = SkAlphaMul(g, a);
+        b = SkAlphaMul(b, a);
+    }
+
+    switch (fConfig) {
+        case kA1_Config: {
+            uint8_t* p = (uint8_t*)fPixels;
+            const int count = (width + 7) >> 3;
+            a = (a >> 7) ? 0xFF : 0;
+            SkASSERT(count <= rowBytes);
+            while (--height >= 0) {
+                memset(p, a, count);
+                p += rowBytes;
+            }
+            break;
+        }
+        case kA8_Config: {
+            uint8_t* p = (uint8_t*)fPixels;
+            while (--height >= 0) {
+                memset(p, a, width);
+                p += rowBytes;
+            }
+            break;
+        }
+        case kARGB_4444_Config:
+        case kRGB_565_Config: {
+            uint16_t* p = (uint16_t*)fPixels;
+            uint16_t v;
+
+            if (kARGB_4444_Config == fConfig) {
+                v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
+            } else {    // kRGB_565_Config
+                v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
+                                b >> (8 - SK_B16_BITS));
+            }
+            while (--height >= 0) {
+                sk_memset16(p, v, width);
+                p = (uint16_t*)((char*)p + rowBytes);
+            }
+            break;
+        }
+        case kARGB_8888_Config: {
+            uint32_t* p = (uint32_t*)fPixels;
+            uint32_t  v = SkPackARGB32(a, r, g, b);
+
+            while (--height >= 0) {
+                sk_memset32(p, v, width);
+                p = (uint32_t*)((char*)p + rowBytes);
+            }
+            break;
+        }
+    }
+
+    this->notifyPixelsChanged();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////
+
+#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());
+
+    switch (bm.getConfig()) {
+        case SkBitmap::kA8_Config:
+        case SkBitmap:: kIndex8_Config:
+            // x is fine as is for the calculation
+            break;
+
+        case SkBitmap::kRGB_565_Config:
+        case SkBitmap::kARGB_4444_Config:
+            x <<= 1;
+            break;
+
+        case SkBitmap::kARGB_8888_Config:
+            x <<= 2;
+            break;
+
+        case SkBitmap::kNo_Config:
+        case SkBitmap::kA1_Config:
+        default:
+            return SUB_OFFSET_FAILURE;
+    }
+    return y * bm.rowBytes() + x;
+}
+
+bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
+        return false;   // no src pixels
+    }
+
+    SkIRect srcRect, r;
+    srcRect.set(0, 0, this->width(), this->height());
+    if (!r.intersect(srcRect, subset)) {
+        return false;   // r is empty (i.e. no intersection)
+    }
+
+    if (kRLE_Index8_Config == fConfig) {
+        SkAutoLockPixels alp(*this);
+        // don't call readyToDraw(), since we can operate w/o a colortable
+        // at this stage
+        if (this->getPixels() == NULL) {
+            return false;
+        }
+        SkBitmap bm;
+
+        bm.setConfig(kIndex8_Config, r.width(), r.height());
+        bm.allocPixels(this->getColorTable());
+        if (NULL == bm.getPixels()) {
+            return false;
+        }
+
+        const RLEPixels* rle = (const RLEPixels*)this->getPixels();
+        uint8_t* dst = bm.getAddr8(0, 0);
+        const int width = bm.width();
+        const int rowBytes = bm.rowBytes();
+
+        for (int y = r.fTop; y < r.fBottom; y++) {
+            SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
+            dst += rowBytes;
+        }
+        result->swap(bm);
+        return true;
+    }
+
+    size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
+    if (SUB_OFFSET_FAILURE == offset) {
+        return false;   // config not supported
+    }
+
+    SkBitmap dst;
+    dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
+    dst.setIsVolatile(this->isVolatile());
+
+    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();)
+
+    // we know we're good, so commit to result
+    result->swap(dst);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+bool SkBitmap::canCopyTo(Config dstConfig) const {
+    if (this->getConfig() == kNo_Config) {
+        return false;
+    }
+
+    bool sameConfigs = (this->config() == dstConfig);
+    switch (dstConfig) {
+        case kA8_Config:
+        case kARGB_4444_Config:
+        case kRGB_565_Config:
+        case kARGB_8888_Config:
+            break;
+        case kA1_Config:
+        case kIndex8_Config:
+            if (!sameConfigs) {
+                return false;
+            }
+            break;
+        default:
+            return false;
+    }
+
+    // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
+    if (this->getConfig() == kA1_Config && !sameConfigs) {
+        return false;
+    }
+
+    return true;
+}
+
+bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
+    if (!this->canCopyTo(dstConfig)) {
+        return false;
+    }
+
+    // if we have a texture, first get those pixels
+    SkBitmap tmpSrc;
+    const SkBitmap* src = this;
+
+    if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
+        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;
+        }
+
+        // fall through to the raster case
+        src = &tmpSrc;
+    }
+
+    // we lock this now, since we may need its colortable
+    SkAutoLockPixels srclock(*src);
+    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;
+    SkAutoUnref au(ctable);
+    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());
+        } else {
+            const char* srcP = reinterpret_cast<const char*>(src->getPixels());
+            char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
+            // to be sure we don't read too much, only copy our logical pixels
+            size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
+            for (int y = 0; y < tmpDst.height(); y++) {
+                memcpy(dstP, srcP, bytesToCopy);
+                srcP += src->rowBytes();
+                dstP += tmpDst.rowBytes();
+            }
+        }
+    } else {
+        // if the src has alpha, we have to clear the dst first
+        if (!src->isOpaque()) {
+            tmpDst.eraseColor(0);
+        }
+
+        SkCanvas canvas(tmpDst);
+        SkPaint  paint;
+
+        paint.setDither(true);
+        canvas.drawBitmap(*src, 0, 0, &paint);
+    }
+
+    tmpDst.setIsOpaque(src->isOpaque());
+
+    dst->swap(tmpDst);
+    return true;
+}
+
+bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
+    if (!this->canCopyTo(dstConfig)) {
+        return false;
+    }
+
+    // If we have a PixelRef, and it supports deep copy, use it.
+    // Currently supported only by texture-backed bitmaps.
+    if (fPixelRef) {
+        SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
+        if (pixelRef) {
+            dst->setConfig(dstConfig, fWidth, fHeight);
+            dst->setPixelRef(pixelRef)->unref();
+            return true;
+        }
+    }
+
+    if (this->getTexture()) {
+        return false;
+    } else {
+        return this->copyTo(dst, dstConfig, NULL);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
+                                 const SkBitmap& src) {
+    x <<= 1;
+    y <<= 1;
+    const SkPMColor* p = src.getAddr32(x, y);
+    const SkPMColor* baseP = p;
+    SkPMColor c, ag, rb;
+
+    c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+
+    p = baseP;
+    if (y < src.height() - 1) {
+        p += src.rowBytes() >> 2;
+    }
+    c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+
+    *dst->getAddr32(x >> 1, y >> 1) =
+        ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
+}
+
+static inline uint32_t expand16(U16CPU c) {
+    return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
+}
+
+// returns dirt in the top 16bits, but we don't care, since we only
+// store the low 16bits.
+static inline U16CPU pack16(uint32_t c) {
+    return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
+}
+
+static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
+                                 const SkBitmap& src) {
+    x <<= 1;
+    y <<= 1;
+    const uint16_t* p = src.getAddr16(x, y);
+    const uint16_t* baseP = p;
+    SkPMColor       c;
+
+    c = expand16(*p);
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c += expand16(*p);
+
+    p = baseP;
+    if (y < src.height() - 1) {
+        p += src.rowBytes() >> 1;
+    }
+    c += expand16(*p);
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c += expand16(*p);
+
+    *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
+}
+
+static uint32_t expand4444(U16CPU c) {
+    return (c & 0xF0F) | ((c & ~0xF0F) << 12);
+}
+
+static U16CPU collaps4444(uint32_t c) {
+    return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
+}
+
+static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
+                                   const SkBitmap& src) {
+    x <<= 1;
+    y <<= 1;
+    const uint16_t* p = src.getAddr16(x, y);
+    const uint16_t* baseP = p;
+    uint32_t        c;
+
+    c = expand4444(*p);
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c += expand4444(*p);
+
+    p = baseP;
+    if (y < src.height() - 1) {
+        p += src.rowBytes() >> 1;
+    }
+    c += expand4444(*p);
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c += expand4444(*p);
+
+    *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
+}
+
+void SkBitmap::buildMipMap(bool forceRebuild) {
+    if (forceRebuild)
+        this->freeMipMap();
+    else if (fMipMap)
+        return; // we're already built
+
+    SkASSERT(NULL == fMipMap);
+
+    void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
+
+    const SkBitmap::Config config = this->getConfig();
+
+    switch (config) {
+        case kARGB_8888_Config:
+            proc = downsampleby2_proc32;
+            break;
+        case kRGB_565_Config:
+            proc = downsampleby2_proc16;
+            break;
+        case kARGB_4444_Config:
+            proc = downsampleby2_proc4444;
+            break;
+        case kIndex8_Config:
+        case kA8_Config:
+        default:
+            return; // don't build mipmaps for these configs
+    }
+
+    SkAutoLockPixels alp(*this);
+    if (!this->readyToDraw()) {
+        return;
+    }
+
+    // whip through our loop to compute the exact size needed
+    size_t  size = 0;
+    int     maxLevels = 0;
+    {
+        int width = this->width();
+        int height = this->height();
+        for (;;) {
+            width >>= 1;
+            height >>= 1;
+            if (0 == width || 0 == height) {
+                break;
+            }
+            size += ComputeRowBytes(config, width) * height;
+            maxLevels += 1;
+        }
+    }
+
+    // nothing to build
+    if (0 == maxLevels) {
+        return;
+    }
+
+    SkBitmap srcBM(*this);
+    srcBM.lockPixels();
+    if (!srcBM.readyToDraw()) {
+        return;
+    }
+
+    MipMap* mm = MipMap::Alloc(maxLevels, size);
+    if (NULL == mm) {
+        return;
+    }
+
+    MipLevel*   level = mm->levels();
+    uint8_t*    addr = (uint8_t*)mm->pixels();
+    int         width = this->width();
+    int         height = this->height();
+    unsigned    rowBytes = this->rowBytes();
+    SkBitmap    dstBM;
+
+    for (int i = 0; i < maxLevels; i++) {
+        width >>= 1;
+        height >>= 1;
+        rowBytes = ComputeRowBytes(config, width);
+
+        level[i].fPixels   = addr;
+        level[i].fWidth    = width;
+        level[i].fHeight   = height;
+        level[i].fRowBytes = rowBytes;
+
+        dstBM.setConfig(config, width, height, rowBytes);
+        dstBM.setPixels(addr);
+
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                proc(&dstBM, x, y, srcBM);
+            }
+        }
+
+        srcBM = dstBM;
+        addr += height * rowBytes;
+    }
+    SkASSERT(addr == (uint8_t*)mm->pixels() + size);
+    fMipMap = mm;
+}
+
+bool SkBitmap::hasMipMap() const {
+    return fMipMap != NULL;
+}
+
+int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
+    if (NULL == fMipMap) {
+        return 0;
+    }
+
+    int level = ComputeMipLevel(sx, sy) >> 16;
+    SkASSERT(level >= 0);
+    if (level <= 0) {
+        return 0;
+    }
+
+    if (level >= fMipMap->fLevelCount) {
+        level = fMipMap->fLevelCount - 1;
+    }
+    if (dst) {
+        const MipLevel& mip = fMipMap->levels()[level - 1];
+        dst->setConfig((SkBitmap::Config)this->config(),
+                       mip.fWidth, mip.fHeight, mip.fRowBytes);
+        dst->setPixels(mip.fPixels);
+    }
+    return level;
+}
+
+SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
+    sx = SkAbs32(sx);
+    sy = SkAbs32(sy);
+    if (sx < sy) {
+        sx = sy;
+    }
+    if (sx < SK_Fixed1) {
+        return 0;
+    }
+    int clz = SkCLZ(sx);
+    SkASSERT(clz >= 1 && clz <= 15);
+    return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
+                           int alphaRowBytes) {
+    SkASSERT(alpha != NULL);
+    SkASSERT(alphaRowBytes >= src.width());
+
+    SkBitmap::Config config = src.getConfig();
+    int              w = src.width();
+    int              h = src.height();
+    int              rb = src.rowBytes();
+
+    SkAutoLockPixels alp(src);
+    if (!src.readyToDraw()) {
+        // zero out the alpha buffer and return
+        while (--h >= 0) {
+            memset(alpha, 0, w);
+            alpha += alphaRowBytes;
+        }
+        return false;
+    }
+
+    if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
+        const uint8_t* s = src.getAddr8(0, 0);
+        while (--h >= 0) {
+            memcpy(alpha, s, w);
+            s += rb;
+            alpha += alphaRowBytes;
+        }
+    } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
+        const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
+        while (--h >= 0) {
+            for (int x = 0; x < w; x++) {
+                alpha[x] = SkGetPackedA32(s[x]);
+            }
+            s = (const SkPMColor*)((const char*)s + rb);
+            alpha += alphaRowBytes;
+        }
+    } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
+        const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
+        while (--h >= 0) {
+            for (int x = 0; x < w; x++) {
+                alpha[x] = SkPacked4444ToA32(s[x]);
+            }
+            s = (const SkPMColor16*)((const char*)s + rb);
+            alpha += alphaRowBytes;
+        }
+    } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
+        SkColorTable* ct = src.getColorTable();
+        if (ct) {
+            const SkPMColor* SK_RESTRICT table = ct->lockColors();
+            const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
+            while (--h >= 0) {
+                for (int x = 0; x < w; x++) {
+                    alpha[x] = SkGetPackedA32(table[s[x]]);
+                }
+                s += rb;
+                alpha += alphaRowBytes;
+            }
+            ct->unlockColors(false);
+        }
+    } else {    // src is opaque, so just fill alpha[] with 0xFF
+        memset(alpha, 0xFF, h * alphaRowBytes);
+    }
+    return true;
+}
+
+#include "SkPaint.h"
+#include "SkMaskFilter.h"
+#include "SkMatrix.h"
+
+bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
+                            Allocator *allocator, SkIPoint* offset) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkBitmap    tmpBitmap;
+    SkMatrix    identity;
+    SkMask      srcM, dstM;
+
+    srcM.fBounds.set(0, 0, this->width(), this->height());
+    srcM.fRowBytes = SkAlign4(this->width());
+    srcM.fFormat = SkMask::kA8_Format;
+
+    SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
+
+    // compute our (larger?) dst bounds if we have a filter
+    if (NULL != filter) {
+        identity.reset();
+        srcM.fImage = NULL;
+        if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
+            goto NO_FILTER_CASE;
+        }
+        dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
+    } else {
+    NO_FILTER_CASE:
+        tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
+                       srcM.fRowBytes);
+        if (!tmpBitmap.allocPixels(allocator, NULL)) {
+            // Allocation of pixels for alpha bitmap failed.
+            SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
+                    tmpBitmap.width(), tmpBitmap.height());
+            return false;
+        }
+        GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
+        if (offset) {
+            offset->set(0, 0);
+        }
+        tmpBitmap.swap(*dst);
+        return true;
+    }
+    srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
+    SkAutoMaskFreeImage srcCleanup(srcM.fImage);
+
+    GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
+    if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
+        goto NO_FILTER_CASE;
+    }
+    SkAutoMaskFreeImage dstCleanup(dstM.fImage);
+
+    tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
+                   dstM.fBounds.height(), dstM.fRowBytes);
+    if (!tmpBitmap.allocPixels(allocator, NULL)) {
+        // Allocation of pixels for alpha bitmap failed.
+        SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
+                tmpBitmap.width(), tmpBitmap.height());
+        return false;
+    }
+    memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
+    if (offset) {
+        offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
+    }
+    SkDEBUGCODE(tmpBitmap.validate();)
+
+    tmpBitmap.swap(*dst);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum {
+    SERIALIZE_PIXELTYPE_NONE,
+    SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
+    SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
+    SERIALIZE_PIXELTYPE_REF_DATA,
+    SERIALIZE_PIXELTYPE_REF_PTR,
+};
+
+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.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 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);
+        }
+    } else {
+        buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+    }
+}
+
+void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
+    this->reset();
+
+    int width = buffer.readInt();
+    int height = buffer.readInt();
+    int rowBytes = buffer.readInt();
+    int config = buffer.readU8();
+
+    this->setConfig((Config)config, width, height, rowBytes);
+    this->setIsOpaque(buffer.readBool());
+
+    int reftype = buffer.readU8();
+    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);
+            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:
+            SkDEBUGFAIL("unrecognized pixeltype in serialized data");
+            sk_throw();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmap::RLEPixels::RLEPixels(int width, int height) {
+    fHeight = height;
+    fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
+    sk_bzero(fYPtrs, height * sizeof(uint8_t*));
+}
+
+SkBitmap::RLEPixels::~RLEPixels() {
+    sk_free(fYPtrs);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+void SkBitmap::validate() const {
+    SkASSERT(fConfig < kConfigCount);
+    SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
+    SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
+    SkASSERT(fPixelLockCount >= 0);
+    SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
+    SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
+
+#if 0   // these asserts are not thread-correct, so disable for now
+    if (fPixelRef) {
+        if (fPixelLockCount > 0) {
+            SkASSERT(fPixelRef->isLocked());
+        } else {
+            SkASSERT(NULL == fPixels);
+            SkASSERT(NULL == fColorTable);
+        }
+    }
+#endif
+}
+#endif
diff --git a/legacy/src/core/SkBitmapProcShader.cpp b/legacy/src/core/SkBitmapProcShader.cpp
new file mode 100644
index 0000000..6d64716
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcShader.cpp
@@ -0,0 +1,329 @@
+
+/*
+ * 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 "SkBitmapProcShader.h"
+#include "SkColorPriv.h"
+#include "SkPixelRef.h"
+
+bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) {
+    switch (bm.config()) {
+        case SkBitmap::kA8_Config:
+        case SkBitmap::kRGB_565_Config:
+        case SkBitmap::kIndex8_Config:
+        case SkBitmap::kARGB_8888_Config:
+    //        if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx))
+                return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src,
+                                       TileMode tmx, TileMode tmy) {
+    fRawBitmap = src;
+    fState.fTileModeX = (uint8_t)tmx;
+    fState.fTileModeY = (uint8_t)tmy;
+    fFlags = 0; // computed in setContext
+}
+
+SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fRawBitmap.unflatten(buffer);
+    fState.fTileModeX = buffer.readU8();
+    fState.fTileModeY = buffer.readU8();
+    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 {
+    if (texture) {
+        *texture = fRawBitmap;
+    }
+    if (texM) {
+        texM->reset();
+    }
+    if (xy) {
+        xy[0] = (TileMode)fState.fTileModeX;
+        xy[1] = (TileMode)fState.fTileModeY;
+    }
+    return kDefault_BitmapType;
+}
+
+void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+
+    fRawBitmap.flatten(buffer);
+    buffer.write8(fState.fTileModeX);
+    buffer.write8(fState.fTileModeY);
+}
+
+static bool only_scale_and_translate(const SkMatrix& matrix) {
+    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+    return (matrix.getType() & ~mask) == 0;
+}
+
+bool SkBitmapProcShader::isOpaque() const {
+    return fRawBitmap.isOpaque();
+}
+
+bool SkBitmapProcShader::setContext(const SkBitmap& device,
+                                    const SkPaint& paint,
+                                    const SkMatrix& matrix) {
+    // do this first, so we have a correct inverse matrix
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    fState.fOrigBitmap = fRawBitmap;
+    fState.fOrigBitmap.lockPixels();
+    if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) {
+        fState.fOrigBitmap.unlockPixels();
+        return false;
+    }
+
+    if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
+        return false;
+    }
+
+    const SkBitmap& bitmap = *fState.fBitmap;
+    bool bitmapIsOpaque = bitmap.isOpaque();
+
+    // update fFlags
+    uint32_t flags = 0;
+    if (bitmapIsOpaque && (255 == this->getPaintAlpha())) {
+        flags |= kOpaqueAlpha_Flag;
+    }
+
+    switch (bitmap.config()) {
+        case SkBitmap::kRGB_565_Config:
+            flags |= (kHasSpan16_Flag | kIntrinsicly16_Flag);
+            break;
+        case SkBitmap::kIndex8_Config:
+        case SkBitmap::kARGB_8888_Config:
+            if (bitmapIsOpaque) {
+                flags |= kHasSpan16_Flag;
+            }
+            break;
+        case SkBitmap::kA8_Config:
+            break;  // never set kHasSpan16_Flag
+        default:
+            break;
+    }
+
+    if (paint.isDither() && bitmap.config() != SkBitmap::kRGB_565_Config) {
+        // gradients can auto-dither in their 16bit sampler, but we don't so
+        // we clear the flag here.
+        flags &= ~kHasSpan16_Flag;
+    }
+
+    // if we're only 1-pixel heigh, and we don't rotate, then we can claim this
+    if (1 == bitmap.height() &&
+            only_scale_and_translate(this->getTotalInverse())) {
+        flags |= kConstInY32_Flag;
+        if (flags & kHasSpan16_Flag) {
+            flags |= kConstInY16_Flag;
+        }
+    }
+
+    fFlags = flags;
+    return true;
+}
+
+#define BUF_MAX     128
+
+#define TEST_BUFFER_OVERRITEx
+
+#ifdef TEST_BUFFER_OVERRITE
+    #define TEST_BUFFER_EXTRA   32
+    #define TEST_PATTERN    0x88888888
+#else
+    #define TEST_BUFFER_EXTRA   0
+#endif
+
+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);
+        return;
+    }
+
+    uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
+    SkBitmapProcState::MatrixProc   mproc = state.fMatrixProc;
+    SkBitmapProcState::SampleProc32 sproc = state.fSampleProc32;
+    int max = fState.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
+
+    SkASSERT(state.fBitmap->getPixels());
+    SkASSERT(state.fBitmap->pixelRef() == NULL ||
+             state.fBitmap->pixelRef()->isLocked());
+
+    for (;;) {
+        int n = count;
+        if (n > max) {
+            n = max;
+        }
+        SkASSERT(n > 0 && n < BUF_MAX*2);
+#ifdef TEST_BUFFER_OVERRITE
+        for (int i = 0; i < TEST_BUFFER_EXTRA; i++) {
+            buffer[BUF_MAX + i] = TEST_PATTERN;
+        }
+#endif
+        mproc(state, buffer, n, x, y);
+#ifdef TEST_BUFFER_OVERRITE
+        for (int j = 0; j < TEST_BUFFER_EXTRA; j++) {
+            SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN);
+        }
+#endif
+        sproc(state, buffer, n, dstC);
+
+        if ((count -= n) == 0) {
+            break;
+        }
+        SkASSERT(count > 0);
+        x += n;
+        dstC += n;
+    }
+}
+
+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);
+        return;
+    }
+
+    uint32_t buffer[BUF_MAX];
+    SkBitmapProcState::MatrixProc   mproc = state.fMatrixProc;
+    SkBitmapProcState::SampleProc16 sproc = state.fSampleProc16;
+    int max = fState.maxCountForBufferSize(sizeof(buffer));
+
+    SkASSERT(state.fBitmap->getPixels());
+    SkASSERT(state.fBitmap->pixelRef() == NULL ||
+             state.fBitmap->pixelRef()->isLocked());
+
+    for (;;) {
+        int n = count;
+        if (n > max) {
+            n = max;
+        }
+        mproc(state, buffer, n, x, y);
+        sproc(state, buffer, n, dstC);
+
+        if ((count -= n) == 0) {
+            break;
+        }
+        x += n;
+        dstC += n;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkUnPreMultiply.h"
+#include "SkColorShader.h"
+#include "SkEmptyShader.h"
+
+// returns true and set color if the bitmap can be drawn as a single color
+// (for efficiency)
+static bool canUseColorShader(const SkBitmap& bm, SkColor* color) {
+    if (1 != bm.width() || 1 != bm.height()) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(bm);
+    if (!bm.readyToDraw()) {
+        return false;
+    }
+
+    switch (bm.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            *color = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(0, 0));
+            return true;
+        case SkBitmap::kRGB_565_Config:
+            *color = SkPixel16ToColor(*bm.getAddr16(0, 0));
+            return true;
+        case SkBitmap::kIndex8_Config:
+            *color = SkUnPreMultiply::PMColorToColor(bm.getIndex8Color(0, 0));
+            return true;
+        default: // just skip the other configs for now
+            break;
+    }
+    return false;
+}
+
+#include "SkTemplatesPriv.h"
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+                                       TileMode tmx, TileMode tmy,
+                                       void* storage, size_t storageSize) {
+    SkShader* shader;
+    SkColor color;
+    if (src.isNull()) {
+        SK_PLACEMENT_NEW(shader, SkEmptyShader, storage, storageSize);
+    }
+    else if (canUseColorShader(src, &color)) {
+        SK_PLACEMENT_NEW_ARGS(shader, SkColorShader, storage, storageSize,
+                              (color));
+    } else {
+        SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+                              storageSize, (src, tmx, tmy));
+    }
+    return shader;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkBitmapProcShader)
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char* gTileModeName[] = {
+    "clamp", "repeat", "mirror"
+};
+
+bool SkBitmapProcShader::toDumpString(SkString* str) const {
+    str->printf("BitmapShader: [%d %d %d",
+                fRawBitmap.width(), fRawBitmap.height(),
+                fRawBitmap.bytesPerPixel());
+
+    // add the pixelref
+    SkPixelRef* pr = fRawBitmap.pixelRef();
+    if (pr) {
+        const char* uri = pr->getURI();
+        if (uri) {
+            str->appendf(" \"%s\"", uri);
+        }
+    }
+
+    // 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;
+}
+
diff --git a/legacy/src/core/SkBitmapProcShader.h b/legacy/src/core/SkBitmapProcShader.h
new file mode 100644
index 0000000..4f8d0c5
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcShader.h
@@ -0,0 +1,54 @@
+
+/*
+ * 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 SkBitmapProcShader_DEFINED
+#define SkBitmapProcShader_DEFINED
+
+#include "SkShader.h"
+#include "SkBitmapProcState.h"
+
+class SkBitmapProcShader : public SkShader {
+public:
+    SkBitmapProcShader(const SkBitmap& src, TileMode tx, TileMode ty);
+
+    // 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;
+
+    static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkBitmapProcShader, (buffer));
+    }
+
+    // override from flattenable
+    virtual bool toDumpString(SkString* str) const;
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR()
+protected:
+    SkBitmapProcShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkBitmap          fRawBitmap;   // experimental for RLE encoding
+    SkBitmapProcState fState;
+    uint32_t          fFlags;
+
+private:
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/legacy/src/core/SkBitmapProcState.cpp b/legacy/src/core/SkBitmapProcState.cpp
new file mode 100644
index 0000000..3d34b20
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcState.cpp
@@ -0,0 +1,571 @@
+
+/*
+ * 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 "SkBitmapProcState.h"
+#include "SkBitmapProcState_filter.h"
+#include "SkColorPriv.h"
+#include "SkFilterProc.h"
+#include "SkPaint.h"
+#include "SkShader.h"   // for tilemodes
+
+// 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)   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"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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)        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"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool valid_for_filtering(unsigned dimension) {
+    // for filtering, width and height must fit in 14bits, since we use steal
+    // 2 bits from each to store our 4bit subpixel data
+    return (dimension & ~0x3FFF) == 0;
+}
+
+bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
+    if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
+        return false;
+    }
+
+    const SkMatrix* m;
+    bool trivial_matrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0;
+    bool clamp_clamp = SkShader::kClamp_TileMode == fTileModeX &&
+                       SkShader::kClamp_TileMode == fTileModeY;
+
+    if (clamp_clamp || trivial_matrix) {
+        m = &inv;
+    } else {
+        fUnitInvMatrix = inv;
+        fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
+        m = &fUnitInvMatrix;
+    }
+
+    fBitmap = &fOrigBitmap;
+    if (fOrigBitmap.hasMipMap()) {
+        int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
+                                                SkScalarToFixed(m->getScaleX()),
+                                                SkScalarToFixed(m->getSkewY()));
+        
+        if (shift > 0) {
+            if (m != &fUnitInvMatrix) {
+                fUnitInvMatrix = *m;
+                m = &fUnitInvMatrix;
+            }
+
+            SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
+            fUnitInvMatrix.postScale(scale, scale);
+            
+            // now point here instead of fOrigBitmap
+            fBitmap = &fMipBitmap;
+        }
+    }
+
+    fInvMatrix      = m;
+    fInvProc        = m->getMapXYProc();
+    fInvType        = m->getType();
+    fInvSx          = SkScalarToFixed(m->getScaleX());
+    fInvKy          = SkScalarToFixed(m->getSkewY());
+
+    fAlphaScale = SkAlpha255To256(paint.getAlpha());
+
+    // pick-up filtering from the paint, but only if the matrix is
+    // more complex than identity/translate (i.e. no need to pay the cost
+    // of filtering if we're not scaled etc.).
+    // 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 &&
+                 valid_for_filtering(fBitmap->width() | fBitmap->height()));
+
+    fShaderProc32 = NULL;
+    fShaderProc16 = NULL;
+    fSampleProc32 = NULL;
+    fSampleProc16 = NULL;
+
+    fMatrixProc = this->chooseMatrixProc(trivial_matrix);
+    if (NULL == fMatrixProc) {
+        return false;
+    }
+
+    ///////////////////////////////////////////////////////////////////////
+    
+    int index = 0;
+    if (fAlphaScale < 256) {  // note: this distinction is not used for D16
+        index |= 1;
+    }
+    if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
+        index |= 2;
+    }
+    if (fDoFilter) {
+        index |= 4;
+    }
+    // bits 3,4,5 encoding the source bitmap format
+    switch (fBitmap->config()) {
+        case SkBitmap::kARGB_8888_Config:
+            index |= 0;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            index |= 8;
+            break;
+        case SkBitmap::kIndex8_Config:
+            index |= 16;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            index |= 24;
+            break;
+        case SkBitmap::kA8_Config:
+            index |= 32;
+            fPaintPMColor = SkPreMultiplyColor(paint.getColor());
+            break;
+        default:
+            return false;
+    }
+
+    static const SampleProc32 gSample32[] = {
+        S32_opaque_D32_nofilter_DXDY,
+        S32_alpha_D32_nofilter_DXDY,
+        S32_opaque_D32_nofilter_DX,
+        S32_alpha_D32_nofilter_DX,
+        S32_opaque_D32_filter_DXDY,
+        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,
+        S16_alpha_D32_nofilter_DX,
+        S16_opaque_D32_filter_DXDY,
+        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,
+        SI8_alpha_D32_nofilter_DX,
+        SI8_opaque_D32_filter_DXDY,
+        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,
+        S4444_alpha_D32_nofilter_DX,
+        S4444_opaque_D32_filter_DXDY,
+        S4444_alpha_D32_filter_DXDY,
+        S4444_opaque_D32_filter_DX,
+        S4444_alpha_D32_filter_DX,
+        
+        // A8 treats alpha/opauqe the same (equally efficient)
+        SA8_alpha_D32_nofilter_DXDY,
+        SA8_alpha_D32_nofilter_DXDY,
+        SA8_alpha_D32_nofilter_DX,
+        SA8_alpha_D32_nofilter_DX,
+        SA8_alpha_D32_filter_DXDY,
+        SA8_alpha_D32_filter_DXDY,
+        SA8_alpha_D32_filter_DX,
+        SA8_alpha_D32_filter_DX
+    };
+    
+    static const SampleProc16 gSample16[] = {
+        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
+    };
+
+    fSampleProc32 = gSample32[index];
+    index >>= 1;    // shift away any opaque/alpha distinction
+    fSampleProc16 = gSample16[index];
+
+    // our special-case shaderprocs
+    if (S16_D16_filter_DX == fSampleProc16) {
+        if (clamp_clamp) {
+            fShaderProc16 = Clamp_S16_D16_filter_DX_shaderproc;
+        } else if (SkShader::kRepeat_TileMode == fTileModeX &&
+                   SkShader::kRepeat_TileMode == fTileModeY) {
+            fShaderProc16 = Repeat_S16_D16_filter_DX_shaderproc;
+        }
+    } else if (SI8_opaque_D32_filter_DX == fSampleProc32 && clamp_clamp) {
+        fShaderProc32 = Clamp_SI8_opaque_D32_filter_DX_shaderproc;
+    }
+
+    // see if our platform has any accelerated overrides
+    this->platformProcs();
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+    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)
+    affine/perspective  filter        N * (Y Y X X)
+ */
+int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
+    int32_t size = static_cast<int32_t>(bufferSize);
+
+    size &= ~3; // only care about 4-byte aligned chunks
+    if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
+        size -= 4;   // the shared Y (or YY) coordinate
+        if (size < 0) {
+            size = 0;
+        }
+        size >>= 1;
+    } else {
+        size >>= 2;
+    }
+
+    if (fDoFilter) {
+        size >>= 1;
+    }
+
+    return size;
+}
+
diff --git a/legacy/src/core/SkBitmapProcState.h b/legacy/src/core/SkBitmapProcState.h
new file mode 100644
index 0000000..c04992b
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcState.h
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 2007 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 SkBitmapProcState_DEFINED
+#define SkBitmapProcState_DEFINED
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+
+class SkPaint;
+
+struct SkBitmapProcState {
+
+    typedef void (*ShaderProc32)(const SkBitmapProcState&, int x, int y,
+                                 SkPMColor[], int count);
+
+    typedef void (*ShaderProc16)(const SkBitmapProcState&, int x, int y,
+                                 uint16_t[], int count);
+
+    typedef void (*MatrixProc)(const SkBitmapProcState&,
+                               uint32_t bitmapXY[],
+                               int count,
+                               int x, int y);
+    
+    typedef void (*SampleProc32)(const SkBitmapProcState&,
+                                 const uint32_t[],
+                                 int count,
+                                 SkPMColor colors[]);
+
+    typedef void (*SampleProc16)(const SkBitmapProcState&,
+                                 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
+
+    const SkBitmap*     fBitmap;            // chooseProcs - orig or mip
+    const SkMatrix*     fInvMatrix;         // chooseProcs
+    SkMatrix::MapXYProc fInvProc;           // chooseProcs
+
+    FixedTileProc       fTileProcX;         // chooseProcs
+    FixedTileProc       fTileProcY;         // chooseProcs
+    IntTileProc         fIntTileProcY;      // chooseProcs
+    SkFixed             fFilterOneX;
+    SkFixed             fFilterOneY;
+
+    SkPMColor           fPaintPMColor;      // chooseProcs - A8 config
+    SkFixed             fInvSx;             // chooseProcs
+    SkFixed             fInvKy;             // chooseProcs
+    uint16_t            fAlphaScale;        // chooseProcs
+    uint8_t             fInvType;           // chooseProcs
+    uint8_t             fTileModeX;         // CONSTRUCTOR
+    uint8_t             fTileModeY;         // CONSTRUCTOR
+    SkBool8             fDoFilter;          // chooseProcs
+
+    /** Platforms implement this, and can optionally overwrite only the
+        following fields:
+
+        fShaderProc32
+        fShaderProc16
+        fMatrixProc
+        fSampleProc32
+        fSampleProc32
+
+        They will already have valid function pointers, so a platform that does
+        not have an accelerated version can just leave that field as is. A valid
+        implementation can do nothing (see SkBitmapProcState_opts_none.cpp)
+     */
+    void platformProcs();
+
+    /** Given the byte size of the index buffer to be passed to the matrix proc,
+        return the maximum number of resulting pixels that can be computed
+        (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;
+
+private:
+    friend class SkBitmapProcShader;
+
+    SkMatrix            fUnitInvMatrix;     // chooseProcs
+    SkBitmap            fOrigBitmap;        // CONSTRUCTOR
+    SkBitmap            fMipBitmap;
+
+    MatrixProc chooseMatrixProc(bool trivial_matrix);
+    bool chooseProcs(const SkMatrix& inv, const SkPaint&);
+};
+
+/*  Macros for packing and unpacking pairs of 16bit values in a 32bit uint.
+    Used to allow access to a stream of uint16_t either one at a time, or
+    2 at a time by unpacking a uint32_t
+ */
+#ifdef SK_CPU_BENDIAN
+    #define PACK_TWO_SHORTS(pri, sec) ((pri) << 16 | (sec))
+    #define UNPACK_PRIMARY_SHORT(packed)    ((uint32_t)(packed) >> 16)
+    #define UNPACK_SECONDARY_SHORT(packed)  ((packed) & 0xFFFF)
+#else
+    #define PACK_TWO_SHORTS(pri, sec) ((pri) | ((sec) << 16))
+    #define UNPACK_PRIMARY_SHORT(packed)    ((packed) & 0xFFFF)
+    #define UNPACK_SECONDARY_SHORT(packed)  ((uint32_t)(packed) >> 16)
+#endif
+
+#ifdef SK_DEBUG
+    static inline uint32_t pack_two_shorts(U16CPU pri, U16CPU sec) {
+        SkASSERT((uint16_t)pri == pri);
+        SkASSERT((uint16_t)sec == sec);
+        return PACK_TWO_SHORTS(pri, sec);
+    }
+#else
+    #define pack_two_shorts(pri, sec)   PACK_TWO_SHORTS(pri, sec)
+#endif
+
+// These functions are generated via macros, but are exposed here so that
+// platformProcs may test for them by name.
+void S32_opaque_D32_filter_DX(const SkBitmapProcState& s, const uint32_t xy[],
+                              int count, SkPMColor colors[]);
+void S32_alpha_D32_filter_DX(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[],
+                                  int count, int x, int y);
+void ClampX_ClampY_filter_affine(const SkBitmapProcState& s,
+                                 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);
+
+#endif
diff --git a/legacy/src/core/SkBitmapProcState_filter.h b/legacy/src/core/SkBitmapProcState_filter.h
new file mode 100644
index 0000000..f69e17a
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcState_filter.h
@@ -0,0 +1,156 @@
+
+/*
+ * 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.
+ */
+
+
+#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).
+ */
+
+#if defined(__ARM_HAVE_NEON) && !defined(SK_CPU_BENDIAN)
+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"
+                 );
+}
+#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) {
+    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) {
+    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;
+
+    lo = ((lo >> 8) & mask) * alphaScale;
+    hi = ((hi >> 8) & mask) * alphaScale;
+
+    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
+}
+#define Filter_32_opaque    Filter_32_opaque_portable
+#define Filter_32_alpha     Filter_32_alpha_portable
+#endif
+
diff --git a/legacy/src/core/SkBitmapProcState_matrix.h b/legacy/src/core/SkBitmapProcState_matrix.h
new file mode 100644
index 0000000..f427e96
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcState_matrix.h
@@ -0,0 +1,288 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMath.h"
+
+
+#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
+
+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;
+        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
+        }
+        uint16_t* xx = (uint16_t*)xy;
+        for (i = (count & 3); 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
+
+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;
+    
+    for (int i = count; i > 0; --i) {
+        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
+        fx += dx; fy += dy;
+    }
+}
+
+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();
+        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));
+}
+
+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);
+    }
+}
+
+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);
+}
+
+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();
+        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/legacy/src/core/SkBitmapProcState_matrixProcs.cpp b/legacy/src/core/SkBitmapProcState_matrixProcs.cpp
new file mode 100644
index 0000000..bda2438
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -0,0 +1,527 @@
+/* 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 "SkUtils.h"
+
+/*  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
+ */
+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;
+}
+
+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);
+
+#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
+
+#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)
+#if	defined(__ARM_HAVE_NEON)
+    #include "SkBitmapProcState_matrix_repeat.h"
+#else
+    #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)
+#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;
+#else
+    if (x >> 16)
+    {
+        if (x < 0)
+            x = 0;
+        else
+            x = 0xFFFF;
+    }
+#endif
+    return x;
+}
+
+static inline U16CPU fixed_repeat(SkFixed x)
+{
+    return x & 0xFFFF;
+}
+
+static inline U16CPU fixed_mirror(SkFixed x)
+{
+    SkFixed s = x << 15 >> 31;
+    // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+    return (x ^ s) & 0xFFFF;
+}
+
+static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m)
+{
+    if (SkShader::kClamp_TileMode == m)
+        return fixed_clamp;
+    if (SkShader::kRepeat_TileMode == m)
+        return fixed_repeat;
+    SkASSERT(SkShader::kMirror_TileMode == m);
+    return fixed_mirror;
+}
+
+static inline U16CPU int_clamp(int x, int n) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x >= n)
+        x = n - 1;
+    if (x < 0)
+        x = 0;
+#else
+    if ((unsigned)x >= (unsigned)n) {
+        if (x < 0) {
+            x = 0;
+        } else {
+            x = n - 1;
+        }
+    }
+#endif
+    return x;
+}
+
+static inline U16CPU int_repeat(int x, int n) {
+    return sk_int_mod(x, n);
+}
+
+static inline U16CPU int_mirror(int x, int n) {
+    x = sk_int_mod(x, 2 * n);
+    if (x >= n) {
+        x = n + ~(x - n);
+    }
+    return x;
+}
+
+#if 0
+static void test_int_tileprocs() {
+    for (int i = -8; i <= 8; i++) {
+        SkDebugf(" int_mirror(%2d, 3) = %d\n", i, int_mirror(i, 3));
+    }
+}
+#endif
+
+static SkBitmapProcState::IntTileProc choose_int_tile_proc(unsigned tm) {
+    if (SkShader::kClamp_TileMode == tm)
+        return int_clamp;
+    if (SkShader::kRepeat_TileMode == tm)
+        return int_repeat;
+    SkASSERT(SkShader::kMirror_TileMode == tm);
+    return int_mirror;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
+{
+    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);
+        fx += dx+dx;
+        *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
+        fx += dx+dx;
+    }
+    count &= 3;
+#endif
+
+    uint16_t* xx = (uint16_t*)dst;
+    for (i = count; i > 0; --i) {
+        *xx++ = SkToU16(fx >> 16); fx += dx;
+    }
+}
+
+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)
+    {
+        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;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// stores the same as SCALE, but is cheaper to compute. Also since there is no
+// scale, we don't need/have a FILTER version
+
+static void fill_sequential(uint16_t xptr[], int start, int count) {
+#if 1
+    if (reinterpret_cast<intptr_t>(xptr) & 0x2) {
+        *xptr++ = start++;
+        count -= 1;
+    }
+    if (count > 3) {
+        uint32_t* xxptr = reinterpret_cast<uint32_t*>(xptr);
+        uint32_t pattern0 = PACK_TWO_SHORTS(start + 0, start + 1);
+        uint32_t pattern1 = PACK_TWO_SHORTS(start + 2, start + 3);
+        start += count & ~3;
+        int qcount = count >> 2;
+        do {
+            *xxptr++ = pattern0;
+            pattern0 += 0x40004;
+            *xxptr++ = pattern1;
+            pattern1 += 0x40004;
+        } while (--qcount != 0);
+        xptr = reinterpret_cast<uint16_t*>(xxptr);
+        count &= 3;
+    }
+    while (--count >= 0) {
+        *xptr++ = start++;
+    }
+#else
+    for (int i = 0; i < count; i++) {
+        *xptr++ = start++;
+    }
+#endif
+}
+
+static int nofilter_trans_preamble(const SkBitmapProcState& s, uint32_t** xy,
+                                   int x, int y) {
+    SkPoint pt;
+    s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+               SkIntToScalar(y) + SK_ScalarHalf, &pt);
+    **xy = s.fIntTileProcY(SkScalarToFixed(pt.fY) >> 16,
+                           s.fBitmap->height());
+    *xy += 1;   // bump the ptr
+    // return our starting X position
+    return SkScalarToFixed(pt.fX) >> 16;
+}
+
+static void clampx_nofilter_trans(const SkBitmapProcState& s,
+                                  uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
+
+    int xpos = nofilter_trans_preamble(s, &xy, x, y);
+    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;
+
+    // fill before 0 as needed
+    if (xpos < 0) {
+        n = -xpos;
+        if (n > count) {
+            n = count;
+        }
+        memset(xptr, 0, n * sizeof(uint16_t));
+        count -= n;
+        if (0 == count) {
+            return;
+        }
+        xptr += n;
+        xpos = 0;
+    }
+
+    // fill in 0..width-1 if needed
+    if (xpos < width) {
+        n = width - xpos;
+        if (n > count) {
+            n = count;
+        }
+        fill_sequential(xptr, xpos, n);
+        count -= n;
+        if (0 == count) {
+            return;
+        }
+        xptr += n;
+    }
+
+    // fill the remaining with the max value
+    sk_memset16(xptr, width - 1, count);
+}
+
+static void repeatx_nofilter_trans(const SkBitmapProcState& s,
+                                   uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
+
+    int xpos = nofilter_trans_preamble(s, &xy, x, y);
+    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 start = sk_int_mod(xpos, width);
+    int n = width - start;
+    if (n > count) {
+        n = count;
+    }
+    fill_sequential(xptr, start, n);
+    xptr += n;
+    count -= n;
+
+    while (count >= width) {
+        fill_sequential(xptr, 0, width);
+        xptr += width;
+        count -= width;
+    }
+
+    if (count > 0) {
+        fill_sequential(xptr, 0, count);
+    }
+}
+
+static void fill_backwards(uint16_t xptr[], int pos, int count) {
+    for (int i = 0; i < count; i++) {
+        SkASSERT(pos >= 0);
+        xptr[i] = pos--;
+    }
+}
+
+static void mirrorx_nofilter_trans(const SkBitmapProcState& s,
+                                   uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
+
+    int xpos = nofilter_trans_preamble(s, &xy, x, y);
+    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);
+    // need to know our start, and our initial phase (forward or backward)
+    bool forward;
+    int n;
+    int start = sk_int_mod(xpos, 2 * width);
+    if (start >= width) {
+        start = width + ~(start - width);
+        forward = false;
+        n = start + 1;  // [start .. 0]
+    } else {
+        forward = true;
+        n = width - start;  // [start .. width)
+    }
+    if (n > count) {
+        n = count;
+    }
+    if (forward) {
+        fill_sequential(xptr, start, n);
+    } else {
+        fill_backwards(xptr, start, n);
+    }
+    forward = !forward;
+    xptr += n;
+    count -= n;
+    
+    while (count >= width) {
+        if (forward) {
+            fill_sequential(xptr, 0, width);
+        } else {
+            fill_backwards(xptr, width - 1, width);
+        }
+        forward = !forward;
+        xptr += width;
+        count -= width;
+    }
+    
+    if (count > 0) {
+        if (forward) {
+            fill_sequential(xptr, 0, count);
+        } else {
+            fill_backwards(xptr, width - 1, count);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmapProcState::MatrixProc
+SkBitmapProcState::chooseMatrixProc(bool trivial_matrix) {
+//    test_int_tileprocs();
+    // check for our special case when there is no scale/affine/perspective
+    if (trivial_matrix) {
+        SkASSERT(!fDoFilter);
+        fIntTileProcY = choose_int_tile_proc(fTileModeY);
+        switch (fTileModeX) {
+            case SkShader::kClamp_TileMode:
+                return clampx_nofilter_trans;
+            case SkShader::kRepeat_TileMode:
+                return repeatx_nofilter_trans;
+            case SkShader::kMirror_TileMode:
+                return mirrorx_nofilter_trans;
+        }
+    }
+    
+    int index = 0;
+    if (fDoFilter) {
+        index = 1;
+    }
+    if (fInvType & SkMatrix::kPerspective_Mask) {
+        index += 4;
+    } 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];
+    }
+    
+    // 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];
+    }
+    
+    fTileProcX = choose_tile_proc(fTileModeX);
+    fTileProcY = choose_tile_proc(fTileModeY);
+    return GeneralXY_Procs[index];
+}
+
diff --git a/src/core/SkBitmapProcState_matrix_clamp.h b/legacy/src/core/SkBitmapProcState_matrix_clamp.h
similarity index 100%
rename from src/core/SkBitmapProcState_matrix_clamp.h
rename to legacy/src/core/SkBitmapProcState_matrix_clamp.h
diff --git a/src/core/SkBitmapProcState_matrix_repeat.h b/legacy/src/core/SkBitmapProcState_matrix_repeat.h
similarity index 100%
rename from src/core/SkBitmapProcState_matrix_repeat.h
rename to legacy/src/core/SkBitmapProcState_matrix_repeat.h
diff --git a/legacy/src/core/SkBitmapProcState_sample.h b/legacy/src/core/SkBitmapProcState_sample.h
new file mode 100644
index 0000000..e6b587f
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcState_sample.h
@@ -0,0 +1,233 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkUtils.h"
+
+#if DSTSIZE==32
+    #define DSTTYPE SkPMColor
+#elif DSTSIZE==16
+    #define DSTTYPE uint16_t
+#else
+    #error "need DSTSIZE to be 32 or 16"
+#endif
+
+#if (DSTSIZE == 32)
+    #define BITMAPPROC_MEMSET(ptr, value, n) sk_memset32(ptr, value, n)
+#elif (DSTSIZE == 16)
+    #define BITMAPPROC_MEMSET(ptr, value, n) sk_memset16(ptr, value, n)
+#else
+    #error "unsupported DSTSIZE"
+#endif
+
+void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s,
+                              const uint32_t* SK_RESTRICT xy,
+                              int count, DSTTYPE* SK_RESTRICT colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fDoFilter == false);
+    SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+    PREAMBLE(s);
+#endif
+    const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+    int i, rb = s.fBitmap->rowBytes();
+
+    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());
+        src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
+        *colors++ = RETURNDST(src);
+    }
+    if (count & 1) {
+        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);
+    }
+
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+
+void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s,
+                            const uint32_t* SK_RESTRICT xy,
+                            int count, DSTTYPE* SK_RESTRICT colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
+    SkASSERT(s.fDoFilter == false);
+    SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+    PREAMBLE(s);
+#endif
+    const SRCTYPE* SK_RESTRICT srcAddr = (const SRCTYPE*)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 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);
+        BITMAPPROC_MEMSET(colors, dstValue, count);
+    } else {
+        int i;
+        for (i = (count >> 2); i > 0; --i) {
+            uint32_t xx0 = *xy++;
+            uint32_t xx1 = *xy++;
+            SRCTYPE x0 = srcAddr[UNPACK_PRIMARY_SHORT(xx0)];
+            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);
+            *colors++ = RETURNDST(x3);
+        }
+        const uint16_t* SK_RESTRICT xx = (const uint16_t*)(xy);
+        for (i = (count & 3); i > 0; --i) {
+            SkASSERT(*xx < (unsigned)s.fBitmap->width());
+            src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+        }
+    }
+    
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void MAKENAME(_filter_DX)(const SkBitmapProcState& s,
+                          const uint32_t* SK_RESTRICT xy,
+                           int count, DSTTYPE* SK_RESTRICT colors) {
+    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();
+    unsigned rb = s.fBitmap->rowBytes();
+    unsigned subY;
+    const SRCTYPE* SK_RESTRICT row0;
+    const SRCTYPE* SK_RESTRICT row1;
+
+    // setup row ptrs and update proc_table
+    {
+        uint32_t XY = *xy++;
+        unsigned y0 = XY >> 14;
+        row0 = (const SRCTYPE*)(srcAddr + (y0 >> 4) * rb);
+        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;        
+        x0 >>= 4;
+
+        FILTER_PROC(subX, subY,
+                    SRC_TO_FILTER(row0[x0]),
+                    SRC_TO_FILTER(row0[x1]),
+                    SRC_TO_FILTER(row1[x0]),
+                    SRC_TO_FILTER(row1[x1]),
+                    colors);
+        colors += 1;
+
+    } while (--count != 0);
+    
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s,
+                            const uint32_t* SK_RESTRICT xy,
+                            int count, DSTTYPE* SK_RESTRICT colors) {
+    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]),
+                    SRC_TO_FILTER(row1[x0]),
+                    SRC_TO_FILTER(row1[x1]),
+                    colors);
+        colors += 1;
+    } while (--count != 0);
+    
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+
+#undef MAKENAME
+#undef DSTSIZE
+#undef DSTTYPE
+#undef SRCTYPE
+#undef CHECKSTATE
+#undef RETURNDST
+#undef SRC_TO_FILTER
+#undef FILTER_TO_DST
+
+#ifdef PREAMBLE
+    #undef PREAMBLE
+#endif
+#ifdef POSTAMBLE
+    #undef POSTAMBLE
+#endif
+
+#undef FILTER_PROC_TYPE
+#undef GET_FILTER_TABLE
+#undef GET_FILTER_ROW
+#undef GET_FILTER_ROW_PROC
+#undef GET_FILTER_PROC
+#undef BITMAPPROC_MEMSET
diff --git a/legacy/src/core/SkBitmapProcState_shaderproc.h b/legacy/src/core/SkBitmapProcState_shaderproc.h
new file mode 100644
index 0000000..a3a8a99
--- /dev/null
+++ b/legacy/src/core/SkBitmapProcState_shaderproc.h
@@ -0,0 +1,89 @@
+
+/*
+ * 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 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) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fDoFilter);
+    SkDEBUGCODE(CHECKSTATE(s);)
+
+    const unsigned maxX = s.fBitmap->width() - 1;
+    const SkFixed oneX = s.fFilterOneX;
+    const SkFixed dx = s.fInvSx;
+    SkFixed fx;
+    const SRCTYPE* SK_RESTRICT row0;
+    const SRCTYPE* SK_RESTRICT row1;
+    unsigned subY;
+
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                   SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        // compute our two Y values up front
+        subY = TILEY_LOW_BITS(fy, maxY);
+        int y0 = TILEY_PROCF(fy, maxY);
+        int y1 = TILEY_PROCF((fy + s.fFilterOneY), maxY);
+
+        const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+        unsigned rb = s.fBitmap->rowBytes();
+        row0 = (const SRCTYPE*)(srcAddr + y0 * rb);
+        row1 = (const SRCTYPE*)(srcAddr + y1 * rb);
+        // now initialize fx
+        fx = SkScalarToFixed(pt.fX) - (oneX >> 1);
+    }
+
+#ifdef PREAMBLE
+    PREAMBLE(s);
+#endif
+    
+    do {
+        unsigned subX = TILEX_LOW_BITS(fx, maxX);
+        unsigned x0 = TILEX_PROCF(fx, maxX);
+        unsigned x1 = TILEX_PROCF((fx + oneX), maxX);
+
+        FILTER_PROC(subX, subY,
+                    SRC_TO_FILTER(row0[x0]),
+                    SRC_TO_FILTER(row0[x1]),
+                    SRC_TO_FILTER(row1[x0]),
+                    SRC_TO_FILTER(row1[x1]),
+                    colors);
+        colors += 1;
+
+        fx += dx;
+    } while (--count != 0);
+
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#undef TILEX_PROCF
+#undef TILEY_PROCF
+#undef TILEX_LOW_BITS
+#undef TILEY_LOW_BITS
+#undef MAKENAME
+#undef SRCTYPE
+#undef DSTTYPE
+#undef CHECKSTATE
+#undef SRC_TO_FILTER
+#undef FILTER_TO_DST
+#undef PREAMBLE
+#undef POSTAMBLE
+
+#undef SCALE_FILTER_NAME
diff --git a/legacy/src/core/SkBitmapSampler.cpp b/legacy/src/core/SkBitmapSampler.cpp
new file mode 100644
index 0000000..37cbc5a
--- /dev/null
+++ b/legacy/src/core/SkBitmapSampler.cpp
@@ -0,0 +1,415 @@
+
+/*
+ * 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 "SkBitmapSampler.h"
+
+static SkTileModeProc get_tilemode_proc(SkShader::TileMode mode)
+{
+    switch (mode) {
+    case SkShader::kClamp_TileMode:
+        return do_clamp;
+    case SkShader::kRepeat_TileMode:
+        return do_repeat_mod;
+    case SkShader::kMirror_TileMode:
+        return do_mirror_mod;
+    default:
+        SkDEBUGFAIL("unknown mode");
+        return NULL;
+    }
+}
+
+SkBitmapSampler::SkBitmapSampler(const SkBitmap& bm, bool filter,
+                                 SkShader::TileMode tmx, SkShader::TileMode tmy)
+    : fBitmap(bm), fFilterBitmap(filter), fTileModeX(tmx), fTileModeY(tmy)
+{
+    SkASSERT(bm.width() > 0 && bm.height() > 0);
+
+    fMaxX = SkToU16(bm.width() - 1);
+    fMaxY = SkToU16(bm.height() - 1);
+    
+    fTileProcX = get_tilemode_proc(tmx);
+    fTileProcY = get_tilemode_proc(tmy);
+}
+
+void SkBitmapSampler::setPaint(const SkPaint& paint)
+{
+}
+
+class SkNullBitmapSampler : public SkBitmapSampler {
+public:
+    SkNullBitmapSampler(const SkBitmap& bm, bool filter,
+                        SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, filter, tmx, tmy) {}
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const { return 0; }
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+#define BITMAP_CLASSNAME_PREFIX(name)           ARGB32##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   *bitmap.getAddr32(x, y)
+#include "SkBitmapSamplerTemplate.h"
+
+#include "SkColorPriv.h"
+
+#define BITMAP_CLASSNAME_PREFIX(name)           RGB16##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   SkPixel16ToPixel32(*bitmap.getAddr16(x, y))
+#include "SkBitmapSamplerTemplate.h"
+
+#define BITMAP_CLASSNAME_PREFIX(name)           Index8##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   bitmap.getIndex8Color(x, y)
+#include "SkBitmapSamplerTemplate.h"
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+///////////////// The Bilinear versions
+
+#include "SkFilterProc.h"
+
+class ARGB32_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fPtrProcTable = SkGetBilinearFilterPtrProcTable();
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const uint32_t *p00, *p01, *p10, *p11;
+
+        // 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;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr32(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr32(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr32(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr32(tmpx1, tmpy1);
+        }
+
+        SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
+        return proc(p00, p01, p10, p11);
+    }
+    
+private:
+    const SkFilterPtrProc* fPtrProcTable;
+};
+
+class RGB16_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    RGB16_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fProcTable = SkGetBilinearFilterProcTable();
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const uint16_t *p00, *p01, *p10, *p11;
+
+        // 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;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr16(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr16(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr16(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr16(tmpx1, tmpy1);
+        }
+
+        SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
+        uint32_t c = proc(SkExpand_rgb_16(*p00), SkExpand_rgb_16(*p01),
+                          SkExpand_rgb_16(*p10), SkExpand_rgb_16(*p11));
+
+        return SkPixel16ToPixel32((uint16_t)SkCompact_rgb_16(c));
+    }
+    
+private:
+    const SkFilterProc* fProcTable;
+};
+
+// If we had a init/term method on sampler, we could avoid the per-pixel
+// call to lockColors/unlockColors
+
+class Index8_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fPtrProcTable = SkGetBilinearFilterPtrProcTable();
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const SkBitmap* bitmap = &fBitmap;
+
+        const uint8_t *p00, *p01, *p10, *p11;
+
+         // 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;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr8(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr8(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr8(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr8(tmpx1, tmpy1);
+        }
+
+        const SkPMColor* colors = bitmap->getColorTable()->lockColors();
+
+        SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
+        uint32_t c = proc(&colors[*p00], &colors[*p01], &colors[*p10], &colors[*p11]);
+
+        bitmap->getColorTable()->unlockColors(false);
+
+        return c;
+    }
+    
+private:
+    const SkFilterPtrProc* fPtrProcTable;
+};
+
+class A8_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    A8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fProcTable = SkGetBilinearFilterProcTable();
+    }
+
+    virtual void setPaint(const SkPaint& paint)
+    {
+        fColor = SkPreMultiplyColor(paint.getColor());
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const uint8_t *p00, *p01, *p10, *p11;
+
+        // 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;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr8(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr8(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr8(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr8(tmpx1, tmpy1);
+        }
+
+        SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
+        int alpha = proc(*p00, *p01, *p10, *p11);
+        return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
+    }
+    
+private:
+    const SkFilterProc* fProcTable;
+    SkPMColor           fColor;
+};
+
+class A8_NoFilter_Sampler : public SkBitmapSampler {
+public:
+    A8_NoFilter_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, false, tmx, tmy)
+    {
+    }
+
+    virtual void setPaint(const SkPaint& paint)
+    {
+        fColor = SkPreMultiplyColor(paint.getColor());
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, bool doFilter,
+                                         SkShader::TileMode tmx,
+                                         SkShader::TileMode tmy)
+{
+    switch (bm.getConfig()) {
+    case SkBitmap::kARGB_8888_Config:
+        if (doFilter)
+            return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy));
+
+        if (tmx == tmy) {
+            switch (tmx) {
+            case SkShader::kClamp_TileMode:
+                return SkNEW_ARGS(ARGB32_Point_Clamp_Sampler, (bm));
+            case SkShader::kRepeat_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(ARGB32_Point_Repeat_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(ARGB32_Point_Repeat_Mod_Sampler, (bm));
+            case SkShader::kMirror_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(ARGB32_Point_Mirror_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(ARGB32_Point_Mirror_Mod_Sampler, (bm));
+            default:
+                SkDEBUGFAIL("unknown mode");
+            }
+        }
+        else {  // tmx != tmy
+            return SkNEW_ARGS(ARGB32_Point_Sampler, (bm, tmx, tmy));
+        }
+        break;
+
+    case SkBitmap::kRGB_565_Config:
+        if (doFilter)
+            return SkNEW_ARGS(RGB16_Bilinear_Sampler, (bm, tmx, tmy));
+
+        if (tmx == tmy) {
+            switch (tmx) {
+            case SkShader::kClamp_TileMode:
+                return SkNEW_ARGS(RGB16_Point_Clamp_Sampler, (bm));
+            case SkShader::kRepeat_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(RGB16_Point_Repeat_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(RGB16_Point_Repeat_Mod_Sampler, (bm));
+            case SkShader::kMirror_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(RGB16_Point_Mirror_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(RGB16_Point_Mirror_Mod_Sampler, (bm));
+            default:
+                SkDEBUGFAIL("unknown mode");
+            }
+        }
+        else {  // tmx != tmy
+            return SkNEW_ARGS(RGB16_Point_Sampler, (bm, tmx, tmy));
+        }
+        break;
+
+    case SkBitmap::kIndex8_Config:
+        if (doFilter)
+            return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy));
+
+        if (tmx == tmy) {
+            switch (tmx) {
+            case SkShader::kClamp_TileMode:
+                return SkNEW_ARGS(Index8_Point_Clamp_Sampler, (bm));
+            case SkShader::kRepeat_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(Index8_Point_Repeat_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(Index8_Point_Repeat_Mod_Sampler, (bm));
+            case SkShader::kMirror_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(Index8_Point_Mirror_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(Index8_Point_Mirror_Mod_Sampler, (bm));
+            default:
+                SkDEBUGFAIL("unknown mode");
+            }
+        }
+        else {  // tmx != tmy
+            return SkNEW_ARGS(Index8_Point_Sampler, (bm, tmx, tmy));
+        }
+        break;
+
+    case SkBitmap::kA8_Config:
+        if (doFilter)
+            return SkNEW_ARGS(A8_Bilinear_Sampler, (bm, tmx, tmy));
+        else
+            return SkNEW_ARGS(A8_NoFilter_Sampler, (bm, tmx, tmy));
+        break;
+
+    default:
+        SkDEBUGFAIL("unknown device");
+    }
+    return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy));
+}
+
diff --git a/legacy/src/core/SkBitmapSampler.h b/legacy/src/core/SkBitmapSampler.h
new file mode 100644
index 0000000..47ec331
--- /dev/null
+++ b/legacy/src/core/SkBitmapSampler.h
@@ -0,0 +1,162 @@
+
+/*
+ * 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 SkBitmapSampler_DEFINED
+#define SkBitmapSampler_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+typedef int (*SkTileModeProc)(int value, unsigned max);
+
+class SkBitmapSampler {
+public:
+    SkBitmapSampler(const SkBitmap&, bool filter, SkShader::TileMode tmx, SkShader::TileMode tmy);
+    virtual ~SkBitmapSampler() {}
+
+    const SkBitmap&     getBitmap() const { return fBitmap; }
+    bool                getFilterBitmap() const { return fFilterBitmap; }
+    SkShader::TileMode  getTileModeX() const { return fTileModeX; }
+    SkShader::TileMode  getTileModeY() const { return fTileModeY; }
+
+    /** Given a pixel center at [x,y], return the color sample
+    */
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const = 0;
+
+    virtual void setPaint(const SkPaint& paint);
+
+    // This is the factory for finding an optimal subclass
+    static SkBitmapSampler* Create(const SkBitmap&, bool filter,
+                                   SkShader::TileMode tmx, SkShader::TileMode tmy);
+
+protected:
+    const SkBitmap&     fBitmap;
+    uint16_t            fMaxX, fMaxY;
+    bool                fFilterBitmap;
+    SkShader::TileMode  fTileModeX;
+    SkShader::TileMode  fTileModeY;
+    SkTileModeProc      fTileProcX;
+    SkTileModeProc      fTileProcY;
+
+    // illegal
+    SkBitmapSampler& operator=(const SkBitmapSampler&);
+};
+
+static inline int fixed_clamp(SkFixed x)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x >> 16)
+        x = 0xFFFF;
+    if (x < 0)
+        x = 0;
+#else
+    if (x >> 16)
+    {
+        if (x < 0)
+            x = 0;
+        else
+            x = 0xFFFF;
+    }
+#endif
+    return x;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+static inline int fixed_repeat(SkFixed x)
+{
+    return x & 0xFFFF;
+}
+
+static inline int fixed_mirror(SkFixed x)
+{
+    SkFixed s = x << 15 >> 31;
+    // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+    return (x ^ s) & 0xFFFF;
+}
+
+static inline bool is_pow2(int count)
+{
+    SkASSERT(count > 0);
+    return (count & (count - 1)) == 0;
+}
+
+static inline int do_clamp(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0);
+
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (index > (int)max)
+        index = max;
+    if (index < 0)
+        index = 0;
+#else
+    if ((unsigned)index > max)
+    {
+        if (index < 0)
+            index = 0;
+        else
+            index = max;
+    }
+#endif
+    return index;
+}
+
+static inline int do_repeat_mod(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0);
+
+    if ((unsigned)index > max)
+    {
+        if (index < 0)
+            index = max - (~index % (max + 1));
+        else
+            index = index % (max + 1);
+    }
+    return index;
+}
+
+static inline int do_repeat_pow2(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0 && is_pow2(max + 1));
+
+    return index & max;
+}
+
+static inline int do_mirror_mod(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0);
+
+    // have to handle negatives so that
+    // -1 -> 0, -2 -> 1, -3 -> 2, etc.
+    // so we can't just cal abs
+    index ^= index >> 31;
+
+    if ((unsigned)index > max)
+    {
+        int mod = (max + 1) << 1;
+        index = index % mod;
+        if ((unsigned)index > max)
+            index = mod - index - 1;
+    }
+    return index;
+}
+
+static inline int do_mirror_pow2(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0 && is_pow2(max + 1));
+
+    int s = (index & (max + 1)) - 1;
+    s = ~(s >> 31);
+    // at this stage, s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+    return (index ^ s) & max;
+}
+
+#endif
diff --git a/legacy/src/core/SkBitmapSamplerTemplate.h b/legacy/src/core/SkBitmapSamplerTemplate.h
new file mode 100644
index 0000000..7b56af7
--- /dev/null
+++ b/legacy/src/core/SkBitmapSamplerTemplate.h
@@ -0,0 +1,108 @@
+
+/*
+ * 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.
+ */
+
+
+/*  this guy is pulled in multiple times, with the following symbols defined each time:
+
+    #define BITMAP_CLASSNAME_PREFIX(name)           ARGB32##name
+    #defube BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   *bitmap.getAddr32(x, y)
+*/
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Sampler)(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, false, tmx, tmy)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = fTileProcX(SkFixedFloor(x), fMaxX);
+        y = fTileProcY(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_clamp(SkFixedFloor(x), fMaxX);
+        y = do_clamp(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_repeat_pow2(SkFixedFloor(x), fMaxX);
+        y = do_repeat_pow2(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_repeat_mod(SkFixedFloor(x), fMaxX);
+        y = do_repeat_mod(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_mirror_pow2(SkFixedFloor(x), fMaxX);
+        y = do_mirror_pow2(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_mirror_mod(SkFixedFloor(x), fMaxX);
+        y = do_mirror_mod(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+#undef BITMAP_CLASSNAME_PREFIX
+#undef BITMAP_PIXEL_TO_PMCOLOR
diff --git a/legacy/src/core/SkBitmapShader16BilerpTemplate.h b/legacy/src/core/SkBitmapShader16BilerpTemplate.h
new file mode 100644
index 0000000..0769e1c
--- /dev/null
+++ b/legacy/src/core/SkBitmapShader16BilerpTemplate.h
@@ -0,0 +1,245 @@
+
+/*
+ * 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 "SkFilterProc.h"
+
+class BILERP_BITMAP16_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader {
+public:
+    BILERP_BITMAP16_SHADER_CLASS(const SkBitmap& src)
+        : HasSpan16_Sampler_BitmapShader(src, true,
+                                         SkShader::kClamp_TileMode,
+                                         SkShader::kClamp_TileMode)
+    {
+    }
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+    {
+        SkASSERT(count > 0);
+        
+        U8CPU alpha = this->getPaintAlpha();
+
+        const SkMatrix& inv = this->getTotalInverse();
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+
+        BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap);
+
+        const SkFilterProc* proc_table = SkGetBilinearFilterProcTable();
+        const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels();
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    SkFixed fy = *srcXY++ - SK_FixedHalf;
+                    int ix = fx >> 16;
+                    int iy = fy >> 16;
+                    int x = SkClampMax(ix, srcMaxX);
+                    int y = SkClampMax(iy, srcMaxY);
+
+                    const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+                    p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB)) + x;
+                    if ((unsigned)ix < srcMaxX)
+                        p01 += 1;
+                    p10 = p00;
+                    p11 = p01;
+                    if ((unsigned)iy < srcMaxY)
+                    {
+                        p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                        p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                    }
+
+                    SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                    uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+
+                    *dstC++ = expanded_rgb16_to_8888(c, alpha);
+                }
+            }
+        }
+        else    // linear case
+        {
+            SkFixed fx, fy, dx, dy;
+
+            // now init fx, fy, dx, dy
+            {
+                SkPoint srcPt;
+                this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+                fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+                fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+                if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                    (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+                else
+                {
+                    dx = SkScalarToFixed(inv.getScaleX());
+                    dy = SkScalarToFixed(inv.getSkewY());
+                }
+            }
+
+            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 +
+                                                                   SkClampMax(iy, srcMaxY) * srcRB)) +
+                                                                   SkClampMax(ix, srcMaxX);
+                if ((unsigned)ix < srcMaxX)
+                    p01 += 1;
+                p10 = p00;
+                p11 = p01;
+                if ((unsigned)iy < srcMaxY)
+                {
+                    p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                    p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                }
+
+                SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+                *dstC++ = expanded_rgb16_to_8888(c, alpha);
+
+                fx += dx;
+                fy += dy;
+            } while (--count != 0);
+        }
+        BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap);
+    }
+
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
+    {
+        SkASSERT(count > 0);
+
+        const SkMatrix& inv = this->getTotalInverse();
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+
+        BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap);
+
+        const SkFilterProc* proc_table = SkGetBilinearFilterProcTable();
+        const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels();
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    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 +
+                                                                      SkClampMax(iy, srcMaxY) * srcRB)) +
+                                                                      SkClampMax(ix, srcMaxX);
+                    if ((unsigned)ix < srcMaxX)
+                        p01 += 1;
+                    p10 = p00;
+                    p11 = p01;
+                    if ((unsigned)iy < srcMaxY)
+                    {
+                        p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                        p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                    }
+
+                    SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                    uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+                    *dstC++ = SkCompact_rgb_16(c);
+                }
+            }
+        }
+        else    // linear case
+        {
+            SkFixed fx, fy, dx, dy;
+
+            // now init fx, fy, dx, dy
+            {
+                SkPoint srcPt;
+                this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+                fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+                fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+                if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                    (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+                else
+                {
+                    dx = SkScalarToFixed(inv.getScaleX());
+                    dy = SkScalarToFixed(inv.getSkewY());
+                }
+            }
+
+            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 +
+                                                                  SkClampMax(iy, srcMaxY) * srcRB)) +
+                                                                  SkClampMax(ix, srcMaxX);
+                if ((unsigned)ix < srcMaxX)
+                    p01 += 1;
+                p10 = p00;
+                p11 = p01;
+                if ((unsigned)iy < srcMaxY)
+                {
+                    p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                    p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                }
+
+                SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+                *dstC++ = SkCompact_rgb_16(c);
+
+                fx += dx;
+                fy += dy;
+            } while (--count != 0);
+        }
+        BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap);
+    }
+};
+
+#undef BILERP_BITMAP16_SHADER_CLASS
+#undef BILERP_BITMAP16_SHADER_TYPE
+#undef BILERP_BITMAP16_SHADER_PREAMBLE
+#undef BILERP_BITMAP16_SHADER_PIXEL
+#undef BILERP_BITMAP16_SHADER_POSTAMBLE
diff --git a/legacy/src/core/SkBitmapShaderTemplate.h b/legacy/src/core/SkBitmapShaderTemplate.h
new file mode 100644
index 0000000..bfb10d9
--- /dev/null
+++ b/legacy/src/core/SkBitmapShaderTemplate.h
@@ -0,0 +1,306 @@
+
+/*
+ * 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 NOFILTER_BITMAP_SHADER_PREAMBLE
+    #define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb)
+#endif
+#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE
+    #define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap)
+#endif
+#ifndef NOFILTER_BITMAP_SHADER_PREAMBLE16
+    #define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb)
+#endif
+#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE16
+    #define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap)
+#endif
+
+class NOFILTER_BITMAP_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader {
+public:
+    NOFILTER_BITMAP_SHADER_CLASS(const SkBitmap& src)
+        : HasSpan16_Sampler_BitmapShader(src, false,
+                                         NOFILTER_BITMAP_SHADER_TILEMODE,
+                                         NOFILTER_BITMAP_SHADER_TILEMODE)
+    {
+    }
+    
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix)
+    {
+        if (!this->INHERITED::setContext(device, paint, matrix))
+            return false;
+
+#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        this->computeUnitInverse();
+#endif
+        return true;
+    }
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+    {
+        SkASSERT(count > 0);
+
+#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC32
+        if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0)
+        {
+            NOFILTER_BITMAP_SHADER_SPRITEPROC32(this, x, y, dstC, count);
+            return;
+        }
+#endif
+
+        unsigned        scale = SkAlpha255To256(this->getPaintAlpha());
+#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        const SkMatrix& inv = this->getUnitInverse();
+        SkMatrix::MapPtProc invProc = this->getUnitInverseProc();
+#else
+        const SkMatrix& inv = this->getTotalInverse();
+        SkMatrix::MapPtProc invProc = this->getInverseMapPtProc();
+#endif
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+        SkFixed         fx, fy, dx, dy;
+
+        const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels();
+        NOFILTER_BITMAP_SHADER_PREAMBLE(srcBitmap, srcRB);
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+
+/*  Do I need this?
+#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+            fx >>= level;
+            fy >>= level;
+#endif
+*/
+                if (256 == scale)
+                {
+                    while (--count >= 0)
+                    {
+                        fx = *srcXY++;
+                        fy = *srcXY++;
+                        unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                        unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                        *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                    }
+                }
+                else
+                {
+                    while (--count >= 0)
+                    {
+                        fx = *srcXY++;
+                        fy = *srcXY++;
+                        unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                        unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                        uint32_t c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                        *dstC++ = SkAlphaMulQ(c, scale);
+                    }
+                }
+            }
+            return;
+        }
+
+        // now init fx, fy, dx, dy
+        {
+            SkPoint srcPt;
+            invProc(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+            fx = SkScalarToFixed(srcPt.fX);
+            fy = SkScalarToFixed(srcPt.fY);
+
+            if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+            else
+            {
+                dx = SkScalarToFixed(inv.getScaleX());
+                dy = SkScalarToFixed(inv.getSkewY());
+            }
+        }
+
+#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        {   int level = this->getMipLevel() >> 16;
+            fx >>= level;
+            fy >>= level;
+            dx >>= level;
+            dy >>= level;
+        }
+#endif
+
+        if (dy == 0)
+        {
+            int y_index = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+//          SkDEBUGF(("fy = %g, srcMaxY = %d, y_index = %d\n", SkFixedToFloat(fy), srcMaxY, y_index));
+            srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + y_index * srcRB);
+            if (scale == 256)
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    fx += dx;
+                    *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x);
+                }
+            else
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    SkPMColor c = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x);
+                    fx += dx;
+                    *dstC++ = SkAlphaMulQ(c, scale);
+                }
+        }
+        else    // dy != 0
+        {
+            if (scale == 256)
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                    fx += dx;
+                    fy += dy;
+                    *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                }
+            else
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                    SkPMColor c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                    fx += dx;
+                    fy += dy;
+                    *dstC++ = SkAlphaMulQ(c, scale);
+                }
+        }
+
+        NOFILTER_BITMAP_SHADER_POSTAMBLE(srcBitmap);
+    }
+
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
+    {
+        SkASSERT(count > 0);
+        SkASSERT(this->getFlags() & SkShader::kHasSpan16_Flag);
+
+#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC16
+        if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0)
+        {
+            NOFILTER_BITMAP_SHADER_SPRITEPROC16(this, x, y, dstC, count);
+            return;
+        }
+#endif
+
+#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        const SkMatrix& inv = this->getUnitInverse();
+        SkMatrix::MapPtProc invProc = this->getUnitInverseProc();
+#else
+        const SkMatrix& inv = this->getTotalInverse();
+        SkMatrix::MapPtProc invProc = this->getInverseMapPtProc();
+#endif
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+        SkFixed         fx, fy, dx, dy;
+
+        const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels();
+        NOFILTER_BITMAP_SHADER_PREAMBLE16(srcBitmap, srcRB);
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                
+                while (--count >= 0)
+                {
+                    fx = *srcXY++;
+                    fy = *srcXY++;
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                    *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB);
+                }
+            }
+            return;
+        }
+
+        // now init fx, fy, dx, dy
+        {
+            SkPoint srcPt;
+            invProc(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+            fx = SkScalarToFixed(srcPt.fX);
+            fy = SkScalarToFixed(srcPt.fY);
+
+            if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+            else
+            {
+                dx = SkScalarToFixed(inv.getScaleX());
+                dy = SkScalarToFixed(inv.getSkewY());
+            }
+        }
+
+#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        {   int level = this->getMipLevel() >> 16;
+            fx >>= level;
+            fy >>= level;
+            dx >>= level;
+            dy >>= level;
+        }
+#endif
+
+        if (dy == 0)
+        {
+            srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY) * srcRB);
+            do {
+                unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                fx += dx;
+                *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X16(srcPixels, x);
+            } while (--count != 0);
+        }
+        else    // dy != 0
+        {
+            do {
+                unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                fx += dx;
+                fy += dy;
+                *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB);
+            } while (--count != 0);
+        }
+
+        NOFILTER_BITMAP_SHADER_POSTAMBLE16(srcBitmap);
+    }
+private:
+    typedef HasSpan16_Sampler_BitmapShader INHERITED;
+};
+
+#undef NOFILTER_BITMAP_SHADER_CLASS
+#undef NOFILTER_BITMAP_SHADER_TYPE
+#undef NOFILTER_BITMAP_SHADER_PREAMBLE
+#undef NOFILTER_BITMAP_SHADER_POSTAMBLE
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_X      //(x)
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY     //(x, y, rowBytes)
+#undef NOFILTER_BITMAP_SHADER_TILEMODE
+#undef NOFILTER_BITMAP_SHADER_TILEPROC
+
+#undef NOFILTER_BITMAP_SHADER_PREAMBLE16
+#undef NOFILTER_BITMAP_SHADER_POSTAMBLE16
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_X16        //(x)
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY16       //(x, y, rowBytes)
+
+#undef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+#undef NOFILTER_BITMAP_SHADER_SPRITEPROC16
+#undef NOFILTER_BITMAP_SHADER_SPRITEPROC32
diff --git a/legacy/src/core/SkBitmap_scroll.cpp b/legacy/src/core/SkBitmap_scroll.cpp
new file mode 100644
index 0000000..54110e8
--- /dev/null
+++ b/legacy/src/core/SkBitmap_scroll.cpp
@@ -0,0 +1,117 @@
+
+/*
+ * 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 "SkRegion.h"
+
+bool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy,
+                          SkRegion* inval) const
+{
+    if (NULL != subset) {
+        SkBitmap tmp;
+
+        return  this->extractSubset(&tmp, *subset) &&
+                // now call again with no rectangle
+                tmp.scrollRect(NULL, dx, dy, inval);
+    }
+
+    int shift;
+
+    switch (this->config()) {
+    case kIndex8_Config:
+    case kA8_Config:
+        shift = 0;
+        break;
+    case kARGB_4444_Config:
+    case kRGB_565_Config:
+        shift = 1;
+        break;
+    case kARGB_8888_Config:
+        shift = 2;
+        break;
+    default:
+        // can't scroll this config
+        return false;
+    }
+
+    int width = this->width();
+    int height = this->height();    
+    
+    // check if there's nothing to do
+    if ((dx | dy) == 0 || width <= 0 || height <= 0) {
+        if (NULL != inval) {
+            inval->setEmpty();
+        }
+        return true;
+    }
+
+    // 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
+    if (this->getPixels() == NULL) {
+        return true;
+    }
+
+    char*       dst = (char*)this->getPixels();
+    const char* src = dst;
+    int         rowBytes = this->rowBytes();    // need rowBytes to be signed
+
+    if (dy <= 0) {
+        src -= dy * rowBytes;
+        height += dy;
+    } else {
+        dst += dy * rowBytes;
+        height -= dy;
+        // now jump src/dst to the last scanline
+        src += (height - 1) * rowBytes;
+        dst += (height - 1) * rowBytes;
+        // now invert rowbytes so we copy backwards in the loop
+        rowBytes = -rowBytes;
+    }
+
+    if (dx <= 0) {
+        src -= dx << shift;
+        width += dx;
+    } else {
+        dst += dx << shift;
+        width -= dx;
+    }
+
+    // If the X-translation would push it completely beyond the region,
+    // then there's nothing to draw.
+    if (width <= 0) {
+        return true;
+    }
+
+    width <<= shift;    // now width is the number of bytes to move per line
+    while (--height >= 0) {
+        memmove(dst, src, width);
+        dst += rowBytes;
+        src += rowBytes;
+    }
+    return true;
+}
diff --git a/legacy/src/core/SkBlitBWMaskTemplate.h b/legacy/src/core/SkBlitBWMaskTemplate.h
new file mode 100644
index 0000000..ecbdfb3
--- /dev/null
+++ b/legacy/src/core/SkBlitBWMaskTemplate.h
@@ -0,0 +1,129 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkMask.h"
+
+#ifndef ClearLow3Bits_DEFINED
+#define ClearLow3Bits_DEFINED
+    #define ClearLow3Bits(x)    ((unsigned)(x) >> 3 << 3)
+#endif
+
+/*
+    SK_BLITBWMASK_NAME          name of function(const SkBitmap& bitmap, const SkMask& mask, const SkIRect& clip, SK_BLITBWMASK_ARGS)
+    SK_BLITBWMASK_ARGS          list of additional arguments to SK_BLITBWMASK_NAME, beginning with a comma
+    SK_BLITBWMASK_BLIT8         name of function(U8CPU byteMask, SK_BLITBWMASK_DEVTYPE* dst, int x, int y)
+    SK_BLITBWMASK_GETADDR       either getAddr32 or getAddr16 or getAddr8
+    SK_BLITBWMASK_DEVTYPE       either U32 or U16 or U8
+*/
+
+static void SK_BLITBWMASK_NAME(const SkBitmap& bitmap, const SkMask& srcMask, const SkIRect& clip SK_BLITBWMASK_ARGS)
+{
+    SkASSERT(clip.fRight <= srcMask.fBounds.fRight);
+
+    int cx = clip.fLeft;
+    int cy = clip.fTop;
+    int maskLeft = srcMask.fBounds.fLeft;
+    unsigned mask_rowBytes = srcMask.fRowBytes;
+    unsigned bitmap_rowBytes = bitmap.rowBytes();
+    unsigned height = clip.height();
+
+    SkASSERT(mask_rowBytes != 0);
+    SkASSERT(bitmap_rowBytes != 0);
+    SkASSERT(height != 0);
+
+    const uint8_t* bits = srcMask.getAddr1(cx, cy);
+    SK_BLITBWMASK_DEVTYPE* device = bitmap.SK_BLITBWMASK_GETADDR(cx, cy);
+
+    if (cx == maskLeft && clip.fRight == srcMask.fBounds.fRight)
+    {
+        do {
+            SK_BLITBWMASK_DEVTYPE* dst = device;
+            unsigned rb = mask_rowBytes;
+            do {
+                U8CPU mask = *bits++;
+                SK_BLITBWMASK_BLIT8(mask, dst);
+                dst += 8;
+            } while (--rb != 0);
+            device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes);
+        } while (--height != 0);
+    }
+    else
+    {
+        int left_edge = cx - maskLeft;
+        SkASSERT(left_edge >= 0);
+        int rite_edge = clip.fRight - maskLeft;
+        SkASSERT(rite_edge > left_edge);
+
+        int left_mask = 0xFF >> (left_edge & 7);
+        int rite_mask = 0xFF << (8 - (rite_edge & 7));
+        int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3);
+
+        // check for empty right mask, so we don't read off the end (or go slower than we need to)
+        if (rite_mask == 0)
+        {
+            SkASSERT(full_runs >= 0);
+            full_runs -= 1;
+            rite_mask = 0xFF;
+        }
+        if (left_mask == 0xFF)
+            full_runs -= 1;
+
+        // back up manually so we can keep in sync with our byte-aligned src
+        // and not trigger an assert from the getAddr## function
+        device -= left_edge & 7;
+        // have cx reflect our actual starting x-coord
+        cx -= left_edge & 7;
+
+        if (full_runs < 0)
+        {
+            left_mask &= rite_mask;
+            SkASSERT(left_mask != 0);
+            do {
+                U8CPU mask = *bits & left_mask;
+                SK_BLITBWMASK_BLIT8(mask, device);
+                bits += mask_rowBytes;
+                device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes);
+            } while (--height != 0);
+        }
+        else
+        {
+            do {
+                int runs = full_runs;
+                SK_BLITBWMASK_DEVTYPE* dst = device;
+                const uint8_t* b = bits;
+                U8CPU   mask;
+
+                mask = *b++ & left_mask;
+                SK_BLITBWMASK_BLIT8(mask, dst);
+                dst += 8;
+
+                while (--runs >= 0)
+                {
+                    mask = *b++;
+                    SK_BLITBWMASK_BLIT8(mask, dst);
+                    dst += 8;
+                }
+
+                mask = *b & rite_mask;
+                SK_BLITBWMASK_BLIT8(mask, dst);
+
+                bits += mask_rowBytes;
+                device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes);
+            } while (--height != 0);
+        }
+    }
+}   
+
+#undef SK_BLITBWMASK_NAME
+#undef SK_BLITBWMASK_ARGS
+#undef SK_BLITBWMASK_BLIT8
+#undef SK_BLITBWMASK_GETADDR
+#undef SK_BLITBWMASK_DEVTYPE
+#undef SK_BLITBWMASK_DOROWSETUP
diff --git a/legacy/src/core/SkBlitMask.h b/legacy/src/core/SkBlitMask.h
new file mode 100644
index 0000000..9c0fe0f
--- /dev/null
+++ b/legacy/src/core/SkBlitMask.h
@@ -0,0 +1,90 @@
+/*
+ * 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 SkBlitMask_DEFINED
+#define SkBlitMask_DEFINED
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "SkMask.h"
+
+class SkBlitMask {
+public:
+    /**
+     *  Returns true if the device config and mask format were supported.
+     *  else return false (nothing was drawn)
+     */
+    static bool BlitColor(const SkBitmap& device, const SkMask& mask,
+                          const SkIRect& clip, SkColor color);
+
+    /**
+     *  Function pointer that blits the mask into a device (dst) colorized
+     *  by color. The number of pixels to blit is specified by width and height,
+     *  but each scanline is offset by dstRB (rowbytes) and srcRB respectively.
+     */
+    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 
+     *  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, 
+                                     SkPMColor opaqueDst);
+
+    /**
+     *  Function pointer that blits a row of src colors through a row of a mask
+     *  onto a row of dst colors. The RowFactory that returns this function ptr
+     *  will have been told the formats for the mask and the dst.
+     */
+    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.
+     */
+    static BlitLCD16RowProc BlitLCD16RowFactory(bool isOpaque);
+
+    /**
+     *  Return either platform specific optimized blitcolor BlitLCD16RowProc,
+     *  or NULL if no optimized routine is available.
+     */
+    static BlitLCD16RowProc PlatformBlitRowProcs16(bool isOpaque);
+
+    enum RowFlags {
+        kSrcIsOpaque_RowFlag    = 1 << 0
+    };
+
+    /**
+     *  Public entry-point to return a blitmask RowProc.
+     *  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.
+     */
+    static RowProc PlatformRowProcs(SkBitmap::Config, SkMask::Format, RowFlags);
+};
+
+#endif
diff --git a/legacy/src/core/SkBlitMask_D32.cpp b/legacy/src/core/SkBlitMask_D32.cpp
new file mode 100644
index 0000000..c97e9e6
--- /dev/null
+++ b/legacy/src/core/SkBlitMask_D32.cpp
@@ -0,0 +1,594 @@
+#include "SkBlitMask.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+
+static void D32_A8_Color(void* SK_RESTRICT dst, size_t dstRB,
+                         const void* SK_RESTRICT maskPtr, size_t maskRB,
+                         SkColor color, int width, int height) {
+    SkPMColor pmc = SkPreMultiplyColor(color);
+    size_t dstOffset = dstRB - (width << 2);
+    size_t maskOffset = maskRB - width;
+    SkPMColor* SK_RESTRICT device = (SkPMColor *)dst;
+    const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr;
+
+    do {
+        int w = width;
+        do {
+            unsigned aa = *mask++;
+            *device = SkBlendARGB32(pmc, *device, aa);
+            device += 1;
+        } while (--w != 0);
+        device = (uint32_t*)((char*)device + dstOffset);
+        mask += maskOffset;
+    } while (--height != 0);
+}
+
+static void D32_A8_Opaque(void* SK_RESTRICT dst, size_t dstRB,
+                          const void* SK_RESTRICT maskPtr, size_t maskRB,
+                          SkColor color, int width, int height) {
+    SkPMColor pmc = SkPreMultiplyColor(color);
+    SkPMColor* SK_RESTRICT device = (SkPMColor*)dst;
+    const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr;
+
+    maskRB -= width;
+    dstRB -= (width << 2);
+    do {
+        int w = width;
+        do {
+            unsigned aa = *mask++;
+            *device = SkAlphaMulQ(pmc, SkAlpha255To256(aa)) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa));
+            device += 1;
+        } while (--w != 0);
+        device = (uint32_t*)((char*)device + dstRB);
+        mask += maskRB;
+    } while (--height != 0);
+}
+
+static void D32_A8_Black(void* SK_RESTRICT dst, size_t dstRB,
+                         const void* SK_RESTRICT maskPtr, size_t maskRB,
+                         SkColor, int width, int height) {
+    SkPMColor* SK_RESTRICT device = (SkPMColor*)dst;
+    const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr;
+
+    maskRB -= width;
+    dstRB -= (width << 2);
+    do {
+        int w = width;
+        do {
+            unsigned aa = *mask++;
+            *device = (aa << SK_A32_SHIFT) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa));
+            device += 1;
+        } while (--w != 0);
+        device = (uint32_t*)((char*)device + dstRB);
+        mask += maskRB;
+    } while (--height != 0);
+}
+
+SkBlitMask::BlitLCD16RowProc SkBlitMask::BlitLCD16RowFactory(bool isOpaque) {
+    BlitLCD16RowProc proc = PlatformBlitRowProcs16(isOpaque);
+    if (proc) {
+        return proc;
+    }
+    
+    if (isOpaque) {
+        return  SkBlitLCD16OpaqueRow;
+    } else {
+        return  SkBlitLCD16Row;
+    }
+}
+
+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;
+    const uint16_t* srcRow = (const uint16_t*)mask;
+    SkPMColor       opaqueDst;
+    
+    SkBlitMask::BlitLCD16RowProc proc = NULL;
+    bool isOpaque = (0xFF == SkColorGetA(color));
+    proc = SkBlitMask::BlitLCD16RowFactory(isOpaque);
+    SkASSERT(proc != NULL);
+
+    if (isOpaque) {
+        opaqueDst = SkPreMultiplyColor(color);
+    } else {
+        opaqueDst = 0;  // ignored
+    }
+    
+    do {
+        proc(dstRow, srcRow, color, width, opaqueDst);
+        dstRow = (SkPMColor*)((char*)dstRow + dstRB);
+        srcRow = (const uint16_t*)((const char*)srcRow + maskRB);
+    } while (--height != 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void blit_lcd32_opaque_row(SkPMColor* SK_RESTRICT dst,
+                                  const SkPMColor* SK_RESTRICT src,
+                                  SkColor color, int width) {
+    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,
+                              SkAlphaBlend(srcR, dstR, maskR),
+                              SkAlphaBlend(srcG, dstG, maskG),
+                              SkAlphaBlend(srcB, dstB, maskB));
+    }
+}
+
+static void blit_lcd32_row(SkPMColor* SK_RESTRICT dst,
+                           const SkPMColor* SK_RESTRICT src,
+                           SkColor color, int width) {
+    int srcA = SkColorGetA(color);
+    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,
+                              SkAlphaBlend(srcR, dstR, maskR),
+                              SkAlphaBlend(srcG, dstG, maskG),
+                              SkAlphaBlend(srcB, dstB, maskB));
+    }
+}
+
+static void D32_LCD32_Blend(void* SK_RESTRICT dst, size_t dstRB,
+                            const void* SK_RESTRICT mask, size_t maskRB,
+                            SkColor color, int width, int height) {
+    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);
+        srcRow = (const SkPMColor*)((const char*)srcRow + maskRB);
+    } while (--height != 0);
+}
+
+static void D32_LCD32_Opaque(void* SK_RESTRICT dst, size_t dstRB,
+                             const void* SK_RESTRICT mask, size_t maskRB,
+                             SkColor color, int width, int height) {
+    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);
+        srcRow = (const SkPMColor*)((const char*)srcRow + maskRB);
+    } while (--height != 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBlitMask::ColorProc D32_A8_Factory(SkColor color) {
+    if (SK_ColorBLACK == color) {
+        return D32_A8_Black;
+    } else if (0xFF == SkColorGetA(color)) {
+        return D32_A8_Opaque;
+    } else {
+        return D32_A8_Color;
+    }
+}
+
+static SkBlitMask::ColorProc D32_LCD32_Factory(SkColor color) {
+    return (0xFF == SkColorGetA(color)) ? D32_LCD32_Opaque : D32_LCD32_Blend;
+}
+
+SkBlitMask::ColorProc SkBlitMask::ColorFactory(SkBitmap::Config config,
+                                               SkMask::Format format,
+                                               SkColor color) {
+    ColorProc proc = PlatformColorProcs(config, format, color);
+    if (proc) {
+        return proc;
+    }
+
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            switch (format) {
+                case SkMask::kA8_Format:
+                    return D32_A8_Factory(color);
+                case SkMask::kLCD16_Format:
+                    return D32_LCD16_Proc;
+                case SkMask::kLCD32_Format:
+                    return D32_LCD32_Factory(color);
+                default:
+                    break;
+            }
+            break;
+        default:
+            break;
+    }
+    return NULL;
+}
+
+bool SkBlitMask::BlitColor(const SkBitmap& device, const SkMask& mask,
+                           const SkIRect& clip, SkColor color) {
+    ColorProc proc = ColorFactory(device.config(), mask.fFormat, color);
+    if (proc) {
+        int x = clip.fLeft;
+        int y = clip.fTop;
+        proc(device.getAddr32(x, y), device.rowBytes(), mask.getAddr(x, y),
+             mask.fRowBytes, color, clip.width(), clip.height());
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void BW_RowProc_Blend(SkPMColor* SK_RESTRICT dst,
+                             const uint8_t* SK_RESTRICT mask,
+                             const SkPMColor* SK_RESTRICT src, int count) {
+    int i, octuple = (count + 7) >> 3;
+    for (i = 0; i < octuple; ++i) {
+        int m = *mask++;
+        if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
+        if (m & 0x40) { dst[1] = SkPMSrcOver(src[1], dst[1]); }
+        if (m & 0x20) { dst[2] = SkPMSrcOver(src[2], dst[2]); }
+        if (m & 0x10) { dst[3] = SkPMSrcOver(src[3], dst[3]); }
+        if (m & 0x08) { dst[4] = SkPMSrcOver(src[4], dst[4]); }
+        if (m & 0x04) { dst[5] = SkPMSrcOver(src[5], dst[5]); }
+        if (m & 0x02) { dst[6] = SkPMSrcOver(src[6], dst[6]); }
+        if (m & 0x01) { dst[7] = SkPMSrcOver(src[7], dst[7]); }
+        src += 8;
+        dst += 8;
+    }
+    count &= 7;
+    if (count > 0) {
+        int m = *mask;
+        do {
+            if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
+            m <<= 1;
+            src += 1;
+            dst += 1;
+        } while (--count > 0);
+    }
+}
+
+static void BW_RowProc_Opaque(SkPMColor* SK_RESTRICT dst,
+                              const uint8_t* SK_RESTRICT mask,
+                              const SkPMColor* SK_RESTRICT src, int count) {
+    int i, octuple = (count + 7) >> 3;
+    for (i = 0; i < octuple; ++i) {
+        int m = *mask++;
+        if (m & 0x80) { dst[0] = src[0]; }
+        if (m & 0x40) { dst[1] = src[1]; }
+        if (m & 0x20) { dst[2] = src[2]; }
+        if (m & 0x10) { dst[3] = src[3]; }
+        if (m & 0x08) { dst[4] = src[4]; }
+        if (m & 0x04) { dst[5] = src[5]; }
+        if (m & 0x02) { dst[6] = src[6]; }
+        if (m & 0x01) { dst[7] = src[7]; }
+        src += 8;
+        dst += 8;
+    }
+    count &= 7;
+    if (count > 0) {
+        int m = *mask;
+        do {
+            if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
+            m <<= 1;
+            src += 1;
+            dst += 1;
+        } while (--count > 0);
+    }
+}
+
+static void A8_RowProc_Blend(SkPMColor* SK_RESTRICT dst,
+                             const uint8_t* SK_RESTRICT mask,
+                             const SkPMColor* SK_RESTRICT src, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (mask[i]) {
+            dst[i] = SkBlendARGB32(src[i], dst[i], mask[i]);
+        }
+    }
+}
+
+// expand the steps that SkAlphaMulQ performs, but this way we can
+//  exand.. add.. combine
+// instead of
+// expand..combine add expand..combine
+//
+#define EXPAND0(v, m, s)    ((v) & (m)) * (s)
+#define EXPAND1(v, m, s)    (((v) >> 8) & (m)) * (s)
+#define COMBINE(e0, e1, m)  ((((e0) >> 8) & (m)) | ((e1) & ~(m)))
+
+static void A8_RowProc_Opaque(SkPMColor* SK_RESTRICT dst,
+                              const uint8_t* SK_RESTRICT mask,
+                              const SkPMColor* SK_RESTRICT src, int count) {
+    const uint32_t rbmask = gMask_00FF00FF;
+    for (int i = 0; i < count; ++i) {
+        int m = mask[i];
+        if (m) {
+            m += (m >> 7);
+#if 1
+            // this is slightly slower than the expand/combine version, but it
+            // is much closer to the old results, so we use it for now to reduce
+            // rebaselining.
+            dst[i] = SkAlphaMulQ(src[i], m) + SkAlphaMulQ(dst[i], 256 - m);
+#else
+            uint32_t v = src[i];
+            uint32_t s0 = EXPAND0(v, rbmask, m);
+            uint32_t s1 = EXPAND1(v, rbmask, m);
+            v = dst[i];
+            uint32_t d0 = EXPAND0(v, rbmask, m);
+            uint32_t d1 = EXPAND1(v, rbmask, m);
+            dst[i] = COMBINE(s0 + d0, s1 + d1, rbmask);
+#endif
+        }
+    }
+}
+
+static int upscale31To255(int value) {
+    value = (value << 3) | (value >> 2);
+    return value;
+}
+
+static int mul(int a, int b) {
+    return a * b >> 8;
+}
+
+static int src_alpha_blend(int src, int dst, int srcA, int mask) {
+    
+    return dst + mul(src - mul(srcA, dst), mask);
+}
+
+static void LCD16_RowProc_Blend(SkPMColor* SK_RESTRICT dst,
+                                const uint16_t* SK_RESTRICT mask,
+                                const SkPMColor* SK_RESTRICT src, int count) {
+    for (int i = 0; i < count; ++i) {
+        uint16_t m = mask[i];
+        if (0 == m) {
+            continue;
+        }
+        
+        SkPMColor s = src[i];
+        SkPMColor d = dst[i];
+
+        int srcA = SkGetPackedA32(s);
+        int srcR = SkGetPackedR32(s);
+        int srcG = SkGetPackedG32(s);
+        int srcB = SkGetPackedB32(s);
+
+        srcA += srcA >> 7;
+
+        /*  We want all of these in 5bits, hence the shifts in case one of them
+         *  (green) is 6bits.
+         */
+        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,
+                              src_alpha_blend(srcR, dstR, srcA, maskR),
+                              src_alpha_blend(srcG, dstG, srcA, maskG),
+                              src_alpha_blend(srcB, dstB, srcA, maskB));
+    }
+}
+
+static void LCD16_RowProc_Opaque(SkPMColor* SK_RESTRICT dst,
+                                 const uint16_t* SK_RESTRICT mask,
+                                 const SkPMColor* SK_RESTRICT src, int count) {
+    for (int i = 0; i < count; ++i) {
+        uint16_t m = mask[i];
+        if (0 == m) {
+            continue;
+        }
+        
+        SkPMColor s = src[i];
+        SkPMColor d = dst[i];
+        
+        int srcR = SkGetPackedR32(s);
+        int srcG = SkGetPackedG32(s);
+        int srcB = SkGetPackedB32(s);
+
+        /*  We want all of these in 5bits, hence the shifts in case one of them
+         *  (green) is 6bits.
+         */
+        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,
+                              SkBlend32(srcR, dstR, maskR),
+                              SkBlend32(srcG, dstG, maskG),
+                              SkBlend32(srcB, dstB, maskB));
+    }
+}
+
+static void LCD32_RowProc_Blend(SkPMColor* SK_RESTRICT dst,
+                                const SkPMColor* SK_RESTRICT mask,
+                                const SkPMColor* SK_RESTRICT src, int count) {
+    for (int i = 0; i < count; ++i) {
+        SkPMColor m = mask[i];
+        if (0 == m) {
+            continue;
+        }
+
+        SkPMColor s = src[i];
+        int srcA = SkGetPackedA32(s);
+        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);
+
+        // Now upscale them to 0..256
+        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,
+                              src_alpha_blend(srcR, dstR, srcA, maskR),
+                              src_alpha_blend(srcG, dstG, srcA, maskG),
+                              src_alpha_blend(srcB, dstB, srcA, maskB));
+    }
+}
+
+static void LCD32_RowProc_Opaque(SkPMColor* SK_RESTRICT dst,
+                                 const SkPMColor* SK_RESTRICT mask,
+                                 const SkPMColor* SK_RESTRICT src, int count) {
+    for (int i = 0; i < count; ++i) {
+        SkPMColor m = mask[i];
+        if (0 == m) {
+            continue;
+        }
+        
+        SkPMColor s = src[i];
+        SkPMColor d = dst[i];
+
+        int maskR = SkGetPackedR32(m);
+        int maskG = SkGetPackedG32(m);
+        int maskB = SkGetPackedB32(m);
+
+        int srcR = SkGetPackedR32(s);
+        int srcG = SkGetPackedG32(s);
+        int srcB = SkGetPackedB32(s);
+
+        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);
+        maskB = SkAlpha255To256(maskB);
+
+        // LCD blitting is only supported if the dst is known/required
+        // to be opaque
+        dst[i] = SkPackARGB32(0xFF,
+                              SkAlphaBlend(srcR, dstR, maskR),
+                              SkAlphaBlend(srcG, dstG, maskG),
+                              SkAlphaBlend(srcB, dstB, maskB));
+    }
+}
+
+SkBlitMask::RowProc SkBlitMask::RowFactory(SkBitmap::Config config,
+                                           SkMask::Format format,
+                                           RowFlags flags) {
+// make this opt-in until chrome can rebaseline
+    RowProc proc = PlatformRowProcs(config, format, flags);
+    if (proc) {
+        return proc;
+    }
+
+    static const RowProc gProcs[] = {
+        // need X coordinate to handle BW
+        NULL, NULL, //(RowProc)BW_RowProc_Blend,      (RowProc)BW_RowProc_Opaque,
+        (RowProc)A8_RowProc_Blend,      (RowProc)A8_RowProc_Opaque,
+        (RowProc)LCD16_RowProc_Blend,   (RowProc)LCD16_RowProc_Opaque,
+        (RowProc)LCD32_RowProc_Blend,   (RowProc)LCD32_RowProc_Opaque,
+    };
+
+    int index;
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            switch (format) {
+                case SkMask::kBW_Format:    index = 0; break;
+                case SkMask::kA8_Format:    index = 2; break;
+                case SkMask::kLCD16_Format: index = 4; break;
+                case SkMask::kLCD32_Format: index = 6; break;
+                default:
+                    return NULL;
+            }
+            if (flags & kSrcIsOpaque_RowFlag) {
+                index |= 1;
+            }
+            SkASSERT((size_t)index < SK_ARRAY_COUNT(gProcs));
+            return gProcs[index];
+        default:
+            break;
+    }
+    return NULL;
+}
+
diff --git a/legacy/src/core/SkBlitRow_D16.cpp b/legacy/src/core/SkBlitRow_D16.cpp
new file mode 100644
index 0000000..c815468
--- /dev/null
+++ b/legacy/src/core/SkBlitRow_D16.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 "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void S32_D565_Opaque(uint16_t* SK_RESTRICT dst,
+                            const SkPMColor* SK_RESTRICT src, int count,
+                            U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            *dst++ = SkPixel32ToPixel16_ToU16(c);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D565_Blend(uint16_t* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT src, int count,
+                             U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 > alpha);
+
+    if (count > 0) {
+        int scale = SkAlpha255To256(alpha);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            uint16_t d = *dst;
+            *dst++ = SkPackRGB16(
+                    SkAlphaBlend(SkPacked32ToR16(c), SkGetPackedR16(d), scale),
+                    SkAlphaBlend(SkPacked32ToG16(c), SkGetPackedG16(d), scale),
+                    SkAlphaBlend(SkPacked32ToB16(c), SkGetPackedB16(d), scale));
+        } while (--count != 0);
+    }
+}
+
+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);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+//            if (__builtin_expect(c!=0, 1))
+            if (c) {
+                *dst = SkSrcOver32To16(c, *dst);
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D565_Blend(uint16_t* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src, int count,
+                               U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 > alpha);
+    
+    if (count > 0) {
+        do {
+            SkPMColor sc = *src++;
+            SkPMColorAssert(sc);
+            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);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+                               
+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 {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+
+            unsigned dither = DITHER_VALUE(x);
+            *dst++ = SkDitherRGB32To565(c, dither);
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D565_Blend_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) {
+        int scale = SkAlpha255To256(alpha);
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+
+            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);
+        } while (--count != 0);
+    }
+}
+
+static void S32A_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 {
+            SkPMColor c = *src++;
+            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);
+                // 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);
+    }
+}
+
+static void S32A_D565_Blend_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) {
+        int src_scale = SkAlpha255To256(alpha);
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            if (c)
+            {
+                unsigned d = *dst;
+                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;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkBlitRow::Proc gDefault_565_Procs[] = {
+    // no dither
+    S32_D565_Opaque,
+    S32_D565_Blend,
+
+    S32A_D565_Opaque,
+    S32A_D565_Blend,
+
+    // dither
+    S32_D565_Opaque_Dither,
+    S32_D565_Blend_Dither,
+
+    S32A_D565_Opaque_Dither,
+    S32A_D565_Blend_Dither
+};
+
+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
+    flags &= kFlags16_Mask;
+
+    SkBlitRow::Proc proc = NULL;
+
+    switch (config) {
+        case SkBitmap::kRGB_565_Config:
+            proc = PlatformProcs565(flags);
+            if (NULL == proc) {
+                proc = gDefault_565_Procs[flags];
+            }
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            proc = PlatformProcs4444(flags);
+            if (NULL == proc) {
+                proc = SkBlitRow_Factory_4444(flags);
+            }
+            break;
+        default:
+            break;
+    }
+    return proc;
+}
+    
diff --git a/legacy/src/core/SkBlitRow_D32.cpp b/legacy/src/core/SkBlitRow_D32.cpp
new file mode 100644
index 0000000..97aa665
--- /dev/null
+++ b/legacy/src/core/SkBlitRow_D32.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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 "SkBlitRow.h"
+#include "SkBlitMask.h"
+#include "SkColorPriv.h"
+#include "SkUtils.h"
+
+#define UNROLL
+
+static void S32_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                 const SkPMColor* SK_RESTRICT src,
+                                 int count, U8CPU alpha) {
+    SkASSERT(255 == alpha);
+    memcpy(dst, src, count * sizeof(SkPMColor));
+}
+
+static void S32_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                const SkPMColor* SK_RESTRICT src,
+                                int count, U8CPU alpha) {
+    SkASSERT(alpha <= 255);
+    if (count > 0) {
+        unsigned src_scale = SkAlpha255To256(alpha);
+        unsigned dst_scale = 256 - src_scale;
+
+#ifdef UNROLL
+        if (count & 1) {
+            *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale);
+            dst += 1;
+            count -= 1;
+        }
+
+        const SkPMColor* SK_RESTRICT srcEnd = src + count;
+        while (src != srcEnd) {
+            *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale);
+            dst += 1;
+            *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale);
+            dst += 1;
+        }
+#else
+        do {
+            *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
+            src += 1;
+            dst += 1;
+        } while (--count > 0);
+#endif
+    }
+}
+
+//#define TEST_SRC_ALPHA
+
+static void S32A_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                  const SkPMColor* SK_RESTRICT src,
+                                  int count, U8CPU alpha) {
+    SkASSERT(255 == alpha);
+    if (count > 0) {
+#ifdef UNROLL
+        if (count & 1) {
+            *dst = SkPMSrcOver(*(src++), *dst);
+            dst += 1;
+            count -= 1;
+        }
+
+        const SkPMColor* SK_RESTRICT srcEnd = src + count;
+        while (src != srcEnd) {
+            *dst = SkPMSrcOver(*(src++), *dst);
+            dst += 1;
+            *dst = SkPMSrcOver(*(src++), *dst);
+            dst += 1;
+        }
+#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);
+#endif
+    }
+}
+
+static void S32A_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                 const SkPMColor* SK_RESTRICT src,
+                                 int count, U8CPU alpha) {
+    SkASSERT(alpha <= 255);
+    if (count > 0) {
+#ifdef UNROLL
+        if (count & 1) {
+            *dst = SkBlendARGB32(*(src++), *dst, alpha);
+            dst += 1;
+            count -= 1;
+        }
+
+        const SkPMColor* SK_RESTRICT srcEnd = src + count;
+        while (src != srcEnd) {
+            *dst = SkBlendARGB32(*(src++), *dst, alpha);
+            dst += 1;
+            *dst = SkBlendARGB32(*(src++), *dst, alpha);
+            dst += 1;
+        }
+#else
+        do {
+            *dst = SkBlendARGB32(*src, *dst, alpha);
+            src += 1;
+            dst += 1;
+        } while (--count > 0);
+#endif
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkBlitRow::Proc32 gDefault_Procs32[] = {
+    S32_Opaque_BlitRow32,
+    S32_Blend_BlitRow32,
+    S32A_Opaque_BlitRow32,
+    S32A_Blend_BlitRow32
+};
+
+SkBlitRow::Proc32 SkBlitRow::Factory32(unsigned flags) {
+    SkASSERT(flags < SK_ARRAY_COUNT(gDefault_Procs32));
+    // just so we don't crash
+    flags &= kFlags32_Mask;
+
+    SkBlitRow::Proc32 proc = PlatformProcs32(flags);
+    if (NULL == proc) {
+        proc = gDefault_Procs32[flags];
+    }
+    SkASSERT(proc);
+    return proc;
+}
+
+SkBlitRow::Proc32 SkBlitRow::ColorProcFactory() {
+    SkBlitRow::ColorProc proc = PlatformColorProc();
+    if (NULL == proc) {
+        proc = Color32;
+    }
+    SkASSERT(proc);
+    return proc;
+}
+
+void SkBlitRow::Color32(SkPMColor* SK_RESTRICT dst,
+                        const SkPMColor* SK_RESTRICT src,
+                        int count, SkPMColor color) {
+    if (count > 0) {
+        if (0 == color) {
+            if (src != dst) {
+                memcpy(dst, src, count * sizeof(SkPMColor));
+            }
+            return;
+        }
+        unsigned colorA = SkGetPackedA32(color);
+        if (255 == colorA) {
+            sk_memset32(dst, color, count);
+        } else {
+            unsigned scale = 256 - SkAlpha255To256(colorA);
+            do {
+                *dst = color + SkAlphaMulQ(*src, scale);
+                src += 1;
+                dst += 1;
+            } while (--count);
+        }
+    }
+}
+
diff --git a/legacy/src/core/SkBlitRow_D4444.cpp b/legacy/src/core/SkBlitRow_D4444.cpp
new file mode 100644
index 0000000..d66d2b7
--- /dev/null
+++ b/legacy/src/core/SkBlitRow_D4444.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void S32_D4444_Opaque(uint16_t* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT src, int count,
+                             U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+            *dst++ = SkPixel32ToPixel4444(c);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D4444_Blend(uint16_t* SK_RESTRICT dst,
+                            const SkPMColor* SK_RESTRICT src, int count,
+                            U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 > alpha);
+
+    if (count > 0) {
+        unsigned scale16 = SkAlpha255To256(alpha) >> 4;
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            uint32_t src_expand = SkExpand32_4444(c);
+            uint32_t dst_expand = SkExpand_4444(*dst);
+            dst_expand += (src_expand - dst_expand) * scale16 >> 4;
+            *dst++ = SkCompact_4444(dst_expand);
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D4444_Opaque(uint16_t* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src, int count,
+                              U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+//            if (__builtin_expect(c!=0, 1))
+            if (c)
+            {
+                unsigned scale16 = SkAlpha255To256(255 - SkGetPackedA32(c)) >> 4;
+                uint32_t src_expand = SkExpand_8888(c);
+                uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
+                *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D4444_Blend(uint16_t* SK_RESTRICT dst,
+                             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 {
+            SkPMColor sc = *src++;
+            SkPMColorAssert(sc);
+
+            if (sc) {
+                unsigned dst_scale = 16 - (SkGetPackedA32(sc) * src_scale >> 8);
+                uint32_t src_expand = SkExpand32_4444(sc) * src_scale;
+                uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
+                *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+                               
+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);
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D4444_Blend_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) {
+        int scale16 = SkAlpha255To256(alpha) >> 4;
+        DITHER_4444_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            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);
+        } while (--count != 0);
+    }
+}
+
+static void S32A_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);
+            if (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;
+                // convert back to SkPMColor
+                c = SkCompact_8888(src_expand + dst_expand);
+                *dst = SkDitherARGB32To4444(c, d);
+            }
+            dst += 1;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+// need DitherExpand888To4444(expand, dither)
+
+static void S32A_D4444_Blend_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) {
+        int src_scale = SkAlpha255To256(alpha) >> 4;
+        DITHER_4444_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            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;
+                // convert back to SkPMColor
+                c = SkCompact_8888(src_expand + dst_expand);
+                *dst = SkDitherARGB32To4444(c, d);
+            }
+            dst += 1;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkBlitRow::Proc gProcs4444[] = {
+    // 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/legacy/src/core/SkBlitter.cpp b/legacy/src/core/SkBlitter.cpp
new file mode 100644
index 0000000..df25b7c
--- /dev/null
+++ b/legacy/src/core/SkBlitter.cpp
@@ -0,0 +1,983 @@
+
+/*
+ * 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 "SkBlitter.h"
+#include "SkAntiRun.h"
+#include "SkColor.h"
+#include "SkColorFilter.h"
+#include "SkMask.h"
+#include "SkMaskFilter.h"
+#include "SkTemplatesPriv.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+SkBlitter::~SkBlitter() {}
+
+const SkBitmap* SkBlitter::justAnOpaqueColor(uint32_t* value) {
+    return NULL;
+}
+
+void SkBlitter::blitH(int x, int y, int width) {
+    SkDEBUGFAIL("unimplemented");
+}
+
+void SkBlitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                          const int16_t runs[]) {
+    SkDEBUGFAIL("unimplemented");
+}
+
+void SkBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    if (alpha == 255) {
+        this->blitRect(x, y, 1, height);
+    } else {
+        int16_t runs[2];
+        runs[0] = 1;
+        runs[1] = 0;
+
+        while (--height >= 0) {
+            this->blitAntiH(x, y++, &alpha, runs);
+        }
+    }
+}
+
+void SkBlitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(width > 0);
+    while (--height >= 0) {
+        this->blitH(x, y++, width);
+    }
+}
+
+/// Default implementation doesn't check for any easy optimizations
+/// such as alpha == 0 or 255; also uses blitV(), which some subclasses
+/// may not support.
+void SkBlitter::blitAntiRect(int x, int y, int width, int height,
+                             SkAlpha leftAlpha, SkAlpha rightAlpha) {
+    this->blitV(x++, y, height, leftAlpha);
+    if (width > 0) {
+        this->blitRect(x, y, width, height);
+        x += width;
+    }
+    this->blitV(x, y, height, rightAlpha);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static inline void bits_to_runs(SkBlitter* blitter, int x, int y,
+                                const uint8_t bits[],
+                                U8CPU left_mask, int rowBytes,
+                                U8CPU right_mask) {
+    int inFill = 0;
+    int pos = 0;
+
+    while (--rowBytes >= 0) {
+        unsigned b = *bits++ & left_mask;
+        if (rowBytes == 0) {
+            b &= right_mask;
+        }
+
+        for (unsigned test = 0x80; test != 0; test >>= 1) {
+            if (b & test) {
+                if (!inFill) {
+                    pos = x;
+                    inFill = true;
+                }
+            } else {
+                if (inFill) {
+                    blitter->blitH(pos, y, x - pos);
+                    inFill = false;
+                }
+            }
+            x += 1;
+        }
+        left_mask = 0xFF;
+    }
+
+    // final cleanup
+    if (inFill) {
+        blitter->blitH(pos, y, x - pos);
+    }
+}
+
+void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+
+    if (mask.fFormat == SkMask::kBW_Format) {
+        int cx = clip.fLeft;
+        int cy = clip.fTop;
+        int maskLeft = mask.fBounds.fLeft;
+        int mask_rowBytes = mask.fRowBytes;
+        int height = clip.height();
+
+        const uint8_t* bits = mask.getAddr1(cx, cy);
+
+        if (cx == maskLeft && clip.fRight == mask.fBounds.fRight) {
+            while (--height >= 0) {
+                bits_to_runs(this, cx, cy, bits, 0xFF, mask_rowBytes, 0xFF);
+                bits += mask_rowBytes;
+                cy += 1;
+            }
+        } else {
+            int left_edge = cx - maskLeft;
+            SkASSERT(left_edge >= 0);
+            int rite_edge = clip.fRight - maskLeft;
+            SkASSERT(rite_edge > left_edge);
+
+            int left_mask = 0xFF >> (left_edge & 7);
+            int rite_mask = 0xFF << (8 - (rite_edge & 7));
+            int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3);
+
+            // check for empty right mask, so we don't read off the end (or go slower than we need to)
+            if (rite_mask == 0) {
+                SkASSERT(full_runs >= 0);
+                full_runs -= 1;
+                rite_mask = 0xFF;
+            }
+            if (left_mask == 0xFF) {
+                full_runs -= 1;
+            }
+
+            // back up manually so we can keep in sync with our byte-aligned src
+            // have cx reflect our actual starting x-coord
+            cx -= left_edge & 7;
+
+            if (full_runs < 0) {
+                SkASSERT((left_mask & rite_mask) != 0);
+                while (--height >= 0) {
+                    bits_to_runs(this, cx, cy, bits, left_mask, 1, rite_mask);
+                    bits += mask_rowBytes;
+                    cy += 1;
+                }
+            } else {
+                while (--height >= 0) {
+                    bits_to_runs(this, cx, cy, bits, left_mask, full_runs + 2, rite_mask);
+                    bits += mask_rowBytes;
+                    cy += 1;
+                }
+            }
+        }
+    } else {
+        int                         width = clip.width();
+        SkAutoSTMalloc<64, int16_t> runStorage(width + 1);
+        int16_t*                    runs = runStorage.get();
+        const uint8_t*              aa = mask.getAddr8(clip.fLeft, clip.fTop);
+
+        sk_memset16((uint16_t*)runs, 1, width);
+        runs[width] = 0;
+
+        int height = clip.height();
+        int y = clip.fTop;
+        while (--height >= 0) {
+            this->blitAntiH(clip.fLeft, y, aa, runs);
+            aa += mask.fRowBytes;
+            y += 1;
+        }
+    }
+}
+
+/////////////////////// these guys are not virtual, just a helpers
+
+void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) {
+    if (clip.quickReject(mask.fBounds)) {
+        return;
+    }
+
+    SkRegion::Cliperator clipper(clip, mask.fBounds);
+
+    while (!clipper.done()) {
+        const SkIRect& cr = clipper.rect();
+        this->blitMask(mask, cr);
+        clipper.next();
+    }
+}
+
+void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) {
+    SkRegion::Cliperator clipper(clip, rect);
+
+    while (!clipper.done()) {
+        const SkIRect& cr = clipper.rect();
+        this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+        clipper.next();
+    }
+}
+
+void SkBlitter::blitRegion(const SkRegion& clip) {
+    SkRegion::Iterator iter(clip);
+
+    while (!iter.done()) {
+        const SkIRect& cr = iter.rect();
+        this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+        iter.next();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkNullBlitter::blitH(int x, int y, int width) {}
+
+void SkNullBlitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                              const int16_t runs[]) {}
+
+void SkNullBlitter::blitV(int x, int y, int height, SkAlpha alpha) {}
+
+void SkNullBlitter::blitRect(int x, int y, int width, int height) {}
+
+void SkNullBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {}
+
+const SkBitmap* SkNullBlitter::justAnOpaqueColor(uint32_t* value) {
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int compute_anti_width(const int16_t runs[]) {
+    int width = 0;
+
+    for (;;) {
+        int count = runs[0];
+
+        SkASSERT(count >= 0);
+        if (count == 0) {
+            break;
+        }
+        width += count;
+        runs += count;
+    }
+    return width;
+}
+
+static inline bool y_in_rect(int y, const SkIRect& rect) {
+    return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
+}
+
+static inline bool x_in_rect(int x, const SkIRect& rect) {
+    return (unsigned)(x - rect.fLeft) < (unsigned)rect.width();
+}
+
+void SkRectClipBlitter::blitH(int left, int y, int width) {
+    SkASSERT(width > 0);
+
+    if (!y_in_rect(y, fClipRect)) {
+        return;
+    }
+
+    int right = left + width;
+
+    if (left < fClipRect.fLeft) {
+        left = fClipRect.fLeft;
+    }
+    if (right > fClipRect.fRight) {
+        right = fClipRect.fRight;
+    }
+
+    width = right - left;
+    if (width > 0) {
+        fBlitter->blitH(left, y, width);
+    }
+}
+
+void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[],
+                                  const int16_t runs[]) {
+    if (!y_in_rect(y, fClipRect) || left >= fClipRect.fRight) {
+        return;
+    }
+
+    int x0 = left;
+    int x1 = left + compute_anti_width(runs);
+
+    if (x1 <= fClipRect.fLeft) {
+        return;
+    }
+
+    SkASSERT(x0 < x1);
+    if (x0 < fClipRect.fLeft) {
+        int dx = fClipRect.fLeft - x0;
+        SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, dx);
+        runs += dx;
+        aa += dx;
+        x0 = fClipRect.fLeft;
+    }
+
+    SkASSERT(x0 < x1 && runs[x1 - x0] == 0);
+    if (x1 > fClipRect.fRight) {
+        x1 = fClipRect.fRight;
+        SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, x1 - x0);
+        ((int16_t*)runs)[x1 - x0] = 0;
+    }
+
+    SkASSERT(x0 < x1 && runs[x1 - x0] == 0);
+    SkASSERT(compute_anti_width(runs) == x1 - x0);
+
+    fBlitter->blitAntiH(x0, y, aa, runs);
+}
+
+void SkRectClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    SkASSERT(height > 0);
+
+    if (!x_in_rect(x, fClipRect)) {
+        return;
+    }
+
+    int y0 = y;
+    int y1 = y + height;
+
+    if (y0 < fClipRect.fTop) {
+        y0 = fClipRect.fTop;
+    }
+    if (y1 > fClipRect.fBottom) {
+        y1 = fClipRect.fBottom;
+    }
+
+    if (y0 < y1) {
+        fBlitter->blitV(x, y0, y1 - y0, alpha);
+    }
+}
+
+void SkRectClipBlitter::blitRect(int left, int y, int width, int height) {
+    SkIRect    r;
+
+    r.set(left, y, left + width, y + height);
+    if (r.intersect(fClipRect)) {
+        fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+    }
+}
+
+void SkRectClipBlitter::blitAntiRect(int left, int y, int width, int height,
+                                     SkAlpha leftAlpha, SkAlpha rightAlpha) {
+    SkIRect    r;
+
+    // The *true* width of the rectangle blitted is width+2:
+    r.set(left, y, left + width + 2, y + height);
+    if (r.intersect(fClipRect)) {
+        if (r.fLeft != left) {
+            SkASSERT(r.fLeft > left);
+            leftAlpha = 255;
+        }
+        if (r.fRight != left + width + 2) {
+            SkASSERT(r.fRight < left + width + 2);
+            rightAlpha = 255;
+        }
+        if (255 == leftAlpha && 255 == rightAlpha) {
+            fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+        } else if (1 == r.width()) {
+            if (r.fLeft == left) {
+                fBlitter->blitV(r.fLeft, r.fTop, r.height(), leftAlpha);
+            } else {
+                SkASSERT(r.fLeft == left + width + 1);
+                fBlitter->blitV(r.fLeft, r.fTop, r.height(), rightAlpha);
+            }
+        } else {
+            fBlitter->blitAntiRect(r.fLeft, r.fTop, r.width() - 2, r.height(),
+                                   leftAlpha, rightAlpha);
+        }
+    }
+}
+
+void SkRectClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+
+    SkIRect    r = clip;
+
+    if (r.intersect(fClipRect)) {
+        fBlitter->blitMask(mask, r);
+    }
+}
+
+const SkBitmap* SkRectClipBlitter::justAnOpaqueColor(uint32_t* value) {
+    return fBlitter->justAnOpaqueColor(value);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkRgnClipBlitter::blitH(int x, int y, int width) {
+    SkRegion::Spanerator span(*fRgn, y, x, x + width);
+    int left, right;
+
+    while (span.next(&left, &right)) {
+        SkASSERT(left < right);
+        fBlitter->blitH(left, y, right - left);
+    }
+}
+
+void SkRgnClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
+                                 const int16_t runs[]) {
+    int width = compute_anti_width(runs);
+    SkRegion::Spanerator span(*fRgn, y, x, x + width);
+    int left, right;
+    SkDEBUGCODE(const SkIRect& bounds = fRgn->getBounds();)
+
+    int prevRite = x;
+    while (span.next(&left, &right)) {
+        SkASSERT(x <= left);
+        SkASSERT(left < right);
+        SkASSERT(left >= bounds.fLeft && right <= bounds.fRight);
+
+        SkAlphaRuns::Break((int16_t*)runs, (uint8_t*)aa, left - x, right - left);
+
+        // now zero before left
+        if (left > prevRite) {
+            int index = prevRite - x;
+            ((uint8_t*)aa)[index] = 0;   // skip runs after right
+            ((int16_t*)runs)[index] = SkToS16(left - prevRite);
+        }
+
+        prevRite = right;
+    }
+
+    if (prevRite > x) {
+        ((int16_t*)runs)[prevRite - x] = 0;
+
+        if (x < 0) {
+            int skip = runs[0];
+            SkASSERT(skip >= -x);
+            aa += skip;
+            runs += skip;
+            x += skip;
+        }
+        fBlitter->blitAntiH(x, y, aa, runs);
+    }
+}
+
+void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    SkIRect    bounds;
+    bounds.set(x, y, x + 1, y + height);
+
+    SkRegion::Cliperator    iter(*fRgn, bounds);
+
+    while (!iter.done()) {
+        const SkIRect& r = iter.rect();
+        SkASSERT(bounds.contains(r));
+
+        fBlitter->blitV(x, r.fTop, r.height(), alpha);
+        iter.next();
+    }
+}
+
+void SkRgnClipBlitter::blitRect(int x, int y, int width, int height) {
+    SkIRect    bounds;
+    bounds.set(x, y, x + width, y + height);
+
+    SkRegion::Cliperator    iter(*fRgn, bounds);
+
+    while (!iter.done()) {
+        const SkIRect& r = iter.rect();
+        SkASSERT(bounds.contains(r));
+
+        fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+        iter.next();
+    }
+}
+
+void SkRgnClipBlitter::blitAntiRect(int x, int y, int width, int height,
+                                    SkAlpha leftAlpha, SkAlpha rightAlpha) {
+    // The *true* width of the rectangle to blit is width + 2
+    SkIRect    bounds;
+    bounds.set(x, y, x + width + 2, y + height);
+
+    SkRegion::Cliperator    iter(*fRgn, bounds);
+
+    while (!iter.done()) {
+        const SkIRect& r = iter.rect();
+        SkASSERT(bounds.contains(r));
+        SkASSERT(r.fLeft >= x);
+        SkASSERT(r.fRight <= x + width + 2);
+
+        SkAlpha effectiveLeftAlpha = (r.fLeft == x) ? leftAlpha : 255;
+        SkAlpha effectiveRightAlpha = (r.fRight == x + width + 2) ?
+                                      rightAlpha : 255;
+
+        if (255 == effectiveLeftAlpha && 255 == effectiveRightAlpha) {
+            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(), 
+                                effectiveLeftAlpha);
+            } else {
+                SkASSERT(r.fLeft == x + width + 1);
+                fBlitter->blitV(r.fLeft, r.fTop, r.height(),
+                                effectiveRightAlpha);
+            }
+        } else {
+            fBlitter->blitAntiRect(r.fLeft, r.fTop, r.width() - 2, r.height(),
+                                   effectiveLeftAlpha, effectiveRightAlpha);
+        }
+        iter.next();
+    }
+}
+
+
+void SkRgnClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+
+    SkRegion::Cliperator iter(*fRgn, clip);
+    const SkIRect&       r = iter.rect();
+    SkBlitter*           blitter = fBlitter;
+
+    while (!iter.done()) {
+        blitter->blitMask(mask, r);
+        iter.next();
+    }
+}
+
+const SkBitmap* SkRgnClipBlitter::justAnOpaqueColor(uint32_t* value) {
+    return fBlitter->justAnOpaqueColor(value);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip,
+                                   const SkIRect* ir) {
+    if (clip) {
+        const SkIRect& clipR = clip->getBounds();
+
+        if (clip->isEmpty() || (ir && !SkIRect::Intersects(clipR, *ir))) {
+            blitter = &fNullBlitter;
+        } else if (clip->isRect()) {
+            if (ir == NULL || !clipR.contains(*ir)) {
+                fRectBlitter.init(blitter, clipR);
+                blitter = &fRectBlitter;
+            }
+        } else {
+            fRgnBlitter.init(blitter, clip);
+            blitter = &fRgnBlitter;
+        }
+    }
+    return blitter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorShader.h"
+#include "SkColorPriv.h"
+
+class Sk3DShader : public SkShader {
+public:
+    Sk3DShader(SkShader* proxy) : fProxy(proxy) {
+        SkSafeRef(proxy);
+        fMask = NULL;
+    }
+
+    virtual ~Sk3DShader() {
+        SkSafeUnref(fProxy);
+    }
+
+    void setMask(const SkMask* mask) { fMask = mask; }
+
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
+                            const SkMatrix& matrix) {
+        if (fProxy) {
+            return fProxy->setContext(device, paint, matrix);
+        } else {
+            fPMColor = SkPreMultiplyColor(paint.getColor());
+            return this->INHERITED::setContext(device, paint, matrix);
+        }
+    }
+
+    virtual void shadeSpan(int x, int y, SkPMColor span[], int count) {
+        if (fProxy) {
+            fProxy->shadeSpan(x, y, span, count);
+        }
+
+        if (fMask == NULL) {
+            if (fProxy == NULL) {
+                sk_memset32(span, fPMColor, count);
+            }
+            return;
+        }
+
+        SkASSERT(fMask->fBounds.contains(x, y));
+        SkASSERT(fMask->fBounds.contains(x + count - 1, y));
+
+        size_t          size = fMask->computeImageSize();
+        const uint8_t*  alpha = fMask->getAddr8(x, y);
+        const uint8_t*  mulp = alpha + size;
+        const uint8_t*  addp = mulp + size;
+
+        if (fProxy) {
+            for (int i = 0; i < count; i++) {
+                if (alpha[i]) {
+                    SkPMColor c = span[i];
+                    if (c) {
+                        unsigned a = SkGetPackedA32(c);
+                        unsigned r = SkGetPackedR32(c);
+                        unsigned g = SkGetPackedG32(c);
+                        unsigned b = SkGetPackedB32(c);
+
+                        unsigned mul = SkAlpha255To256(mulp[i]);
+                        unsigned add = addp[i];
+
+                        r = SkFastMin32(SkAlphaMul(r, mul) + add, a);
+                        g = SkFastMin32(SkAlphaMul(g, mul) + add, a);
+                        b = SkFastMin32(SkAlphaMul(b, mul) + add, a);
+
+                        span[i] = SkPackARGB32(a, r, g, b);
+                    }
+                } else {
+                    span[i] = 0;
+                }
+            }
+        } else {    // color
+            unsigned a = SkGetPackedA32(fPMColor);
+            unsigned r = SkGetPackedR32(fPMColor);
+            unsigned g = SkGetPackedG32(fPMColor);
+            unsigned b = SkGetPackedB32(fPMColor);
+            for (int i = 0; i < count; i++) {
+                if (alpha[i]) {
+                    unsigned mul = SkAlpha255To256(mulp[i]);
+                    unsigned add = addp[i];
+
+                    span[i] = SkPackARGB32( a,
+                                    SkFastMin32(SkAlphaMul(r, mul) + add, a),
+                                    SkFastMin32(SkAlphaMul(g, mul) + add, a),
+                                    SkFastMin32(SkAlphaMul(b, mul) + add, a));
+                } else {
+                    span[i] = 0;
+                }
+            }
+        }
+    }
+
+    virtual void beginSession() {
+        this->INHERITED::beginSession();
+        if (fProxy) {
+            fProxy->beginSession();
+        }
+    }
+
+    virtual void endSession() {
+        if (fProxy) {
+            fProxy->endSession();
+        }
+        this->INHERITED::endSession();
+    }
+
+protected:
+    Sk3DShader(SkFlattenableReadBuffer& buffer) :
+            INHERITED(buffer) {
+        fProxy = static_cast<SkShader*>(buffer.readFlattenable());
+        fPMColor = buffer.readU32();
+        fMask = NULL;
+    }
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.writeFlattenable(fProxy);
+        buffer.write32(fPMColor);
+    }
+
+    virtual Factory getFactory() {
+        return CreateProc;
+    }
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(Sk3DShader, (buffer));
+    }
+
+    SkShader*       fProxy;
+    SkPMColor       fPMColor;
+    const SkMask*   fMask;
+
+    typedef SkShader INHERITED;
+};
+
+class Sk3DBlitter : public SkBlitter {
+public:
+    Sk3DBlitter(SkBlitter* proxy, Sk3DShader* shader, void (*killProc)(void*))
+            : fProxy(proxy), f3DShader(shader), fKillProc(killProc) {
+        shader->ref();
+    }
+
+    virtual ~Sk3DBlitter() {
+        f3DShader->unref();
+        fKillProc(fProxy);
+    }
+
+    virtual void blitH(int x, int y, int width) {
+        fProxy->blitH(x, y, width);
+    }
+
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
+                           const int16_t runs[]) {
+        fProxy->blitAntiH(x, y, antialias, runs);
+    }
+
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) {
+        fProxy->blitV(x, y, height, alpha);
+    }
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        fProxy->blitRect(x, y, width, height);
+    }
+
+    virtual void blitMask(const SkMask& mask, const SkIRect& clip) {
+        if (mask.fFormat == SkMask::k3D_Format) {
+            f3DShader->setMask(&mask);
+
+            ((SkMask*)&mask)->fFormat = SkMask::kA8_Format;
+            fProxy->blitMask(mask, clip);
+            ((SkMask*)&mask)->fFormat = SkMask::k3D_Format;
+
+            f3DShader->setMask(NULL);
+        } else {
+            fProxy->blitMask(mask, clip);
+        }
+    }
+
+private:
+    SkBlitter*  fProxy;
+    Sk3DShader* f3DShader;
+    void        (*fKillProc)(void*);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkCoreBlitters.h"
+
+class SkAutoCallProc {
+public:
+    typedef void (*Proc)(void*);
+
+    SkAutoCallProc(void* obj, Proc proc)
+    : fObj(obj), fProc(proc) {}
+
+    ~SkAutoCallProc() {
+        if (fObj && fProc) {
+            fProc(fObj);
+        }
+    }
+
+    void* get() const { return fObj; }
+
+    void* detach() {
+        void* obj = fObj;
+        fObj = NULL;
+        return obj;
+    }
+
+private:
+    void*   fObj;
+    Proc    fProc;
+};
+
+static void destroy_blitter(void* blitter) {
+    ((SkBlitter*)blitter)->~SkBlitter();
+}
+
+static void delete_blitter(void* blitter) {
+    SkDELETE((SkBlitter*)blitter);
+}
+
+static bool just_solid_color(const SkPaint& paint) {
+    if (paint.getAlpha() == 0xFF && paint.getColorFilter() == NULL) {
+        SkShader* shader = paint.getShader();
+        if (NULL == shader ||
+            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/** By analyzing the paint (with an xfermode), we may decide we can take
+    special action. This enum lists our possible actions
+ */
+enum XferInterp {
+    kNormal_XferInterp,         // no special interpretation, draw normally
+    kSrcOver_XferInterp,        // draw as if in srcover mode
+    kSkipDrawing_XferInterp     // draw nothing
+};
+
+static XferInterp interpret_xfermode(const SkPaint& paint, SkXfermode* xfer,
+                                     SkBitmap::Config deviceConfig) {
+    SkXfermode::Mode  mode;
+
+    if (SkXfermode::AsMode(xfer, &mode)) {
+        switch (mode) {
+            case SkXfermode::kSrc_Mode:
+                if (just_solid_color(paint)) {
+                    return kSrcOver_XferInterp;
+                }
+                break;
+            case SkXfermode::kDst_Mode:
+                return kSkipDrawing_XferInterp;
+            case SkXfermode::kSrcOver_Mode:
+                return kSrcOver_XferInterp;
+            case SkXfermode::kDstOver_Mode:
+                if (SkBitmap::kRGB_565_Config == deviceConfig) {
+                    return kSkipDrawing_XferInterp;
+                }
+                break;
+            case SkXfermode::kSrcIn_Mode:
+                if (SkBitmap::kRGB_565_Config == deviceConfig &&
+                    just_solid_color(paint)) {
+                    return kSrcOver_XferInterp;
+                }
+                break;
+            case SkXfermode::kDstIn_Mode:
+                if (just_solid_color(paint)) {
+                    return kSkipDrawing_XferInterp;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+    return kNormal_XferInterp;
+}
+
+SkBlitter* SkBlitter::Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& origPaint,
+                             void* storage, size_t storageSize) {
+    SkASSERT(storageSize == 0 || storage != NULL);
+
+    SkBlitter*  blitter = NULL;
+
+    // which check, in case we're being called by a client with a dummy device
+    // (e.g. they have a bounder that always aborts the draw)
+    if (SkBitmap::kNo_Config == device.getConfig()) {
+        SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+        return blitter;
+    }
+
+    SkPaint paint(origPaint);
+    SkShader* shader = paint.getShader();
+    SkColorFilter* cf = paint.getColorFilter();
+    SkXfermode* mode = paint.getXfermode();
+
+    Sk3DShader* shader3D = NULL;
+    if (paint.getMaskFilter() != NULL &&
+            paint.getMaskFilter()->getFormat() == SkMask::k3D_Format) {
+        shader3D = SkNEW_ARGS(Sk3DShader, (shader));
+        paint.setShader(shader3D)->unref();
+        shader = shader3D;
+    }
+
+    if (NULL != mode) {
+        switch (interpret_xfermode(paint, mode, device.config())) {
+            case kSrcOver_XferInterp:
+                mode = NULL;
+                paint.setXfermode(NULL);
+                break;
+            case kSkipDrawing_XferInterp:
+                SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+                return blitter;
+            default:
+                break;
+        }
+    }
+
+    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();
+        } 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);
+            cf = NULL;
+        }
+    }
+
+    if (cf) {
+        SkASSERT(shader);
+        shader = SkNEW_ARGS(SkFilterShader, (shader, cf));
+        paint.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);
+    }
+
+    switch (device.getConfig()) {
+        case SkBitmap::kA1_Config:
+            SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter,
+                                  storage, storageSize, (device, paint));
+            break;
+
+        case SkBitmap::kA8_Config:
+            if (shader) {
+                SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter,
+                                      storage, storageSize, (device, paint));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter,
+                                      storage, storageSize, (device, paint));
+            }
+            break;
+
+        case SkBitmap::kARGB_4444_Config:
+            blitter = SkBlitter_ChooseD4444(device, paint, storage, storageSize);
+            break;
+
+        case SkBitmap::kRGB_565_Config:
+            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) {
+                SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter,
+                                      storage, storageSize, (device, paint));
+            } else if (paint.getAlpha() == 0xFF) {
+                SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_Blitter,
+                                      storage, storageSize, (device, paint));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter,
+                                      storage, storageSize, (device, paint));
+            }
+            break;
+
+        default:
+            SkDEBUGFAIL("unsupported device config");
+            SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+            break;
+    }
+
+    if (shader3D) {
+        void (*proc)(void*) = ((void*)storage == (void*)blitter) ? destroy_blitter : delete_blitter;
+        SkAutoCallProc  tmp(blitter, proc);
+
+        blitter = SkNEW_ARGS(Sk3DBlitter, (blitter, shader3D, proc));
+        (void)tmp.detach();
+    }
+    return blitter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const uint16_t gMask_0F0F = 0xF0F;
+const uint32_t gMask_00FF00FF = 0xFF00FF;
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkShaderBlitter::SkShaderBlitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device) {
+    fShader = paint.getShader();
+    SkASSERT(fShader);
+
+    fShader->ref();
+    fShader->beginSession();
+    fShaderFlags = fShader->getFlags();
+}
+
+SkShaderBlitter::~SkShaderBlitter() {
+    fShader->endSession();
+    fShader->unref();
+}
+
diff --git a/legacy/src/core/SkBlitter_4444.cpp b/legacy/src/core/SkBlitter_4444.cpp
new file mode 100644
index 0000000..ec62523
--- /dev/null
+++ b/legacy/src/core/SkBlitter_4444.cpp
@@ -0,0 +1,481 @@
+
+/*
+ * 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 "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkShader.h"
+#include "SkTemplatesPriv.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+static inline SkPMColor SkBlendARGB4444(SkPMColor16 src, SkPMColor16 dst,
+                                        U8CPU aa) {
+    SkASSERT((unsigned)aa <= 255);
+
+    unsigned src_scale = SkAlpha255To256(aa) >> 4;
+    unsigned dst_scale = SkAlpha15To16(15 - SkAlphaMul4(SkGetPackedA4444(src), src_scale));
+
+    uint32_t src32 = SkExpand_4444(src) * src_scale;
+    uint32_t dst32 = SkExpand_4444(dst) * dst_scale;
+    return SkCompact_4444((src32 + dst32) >> 4);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkARGB4444_Blitter : public SkRasterBlitter {
+public:
+    SkARGB4444_Blitter(const SkBitmap& device, const SkPaint& paint);
+    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 blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+protected:
+    SkPMColor16 fPMColor16, fPMColor16Other;
+    SkPMColor16 fRawColor16, fRawColor16Other;
+    uint8_t     fScale16;
+
+private:
+    // illegal
+    SkARGB4444_Blitter& operator=(const SkARGB4444_Blitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+
+SkARGB4444_Blitter::SkARGB4444_Blitter(const SkBitmap& device,
+                                   const SkPaint& paint) : INHERITED(device) {
+    // cache premultiplied versions in 4444
+    SkPMColor c = SkPreMultiplyColor(paint.getColor());
+    fPMColor16 = SkPixel32ToPixel4444(c);
+    if (paint.isDither()) {
+        fPMColor16Other = SkDitherPixel32To4444(c);
+    } else {
+        fPMColor16Other = fPMColor16;
+    }
+
+    // cache raw versions in 4444
+    fRawColor16 = SkPackARGB4444(0xFF >> 4, SkColorGetR(c) >> 4,
+                                 SkColorGetG(c) >> 4, SkColorGetB(c) >> 4);
+    if (paint.isDither()) {
+        fRawColor16Other = SkDitherARGB32To4444(0xFF, SkColorGetR(c),
+                                                SkColorGetG(c), SkColorGetB(c));
+    } else {
+        fRawColor16Other = fRawColor16;
+    }
+
+    fScale16 = SkAlpha15To16(SkGetPackedA4444(fPMColor16Other));
+    if (16 == fScale16) {
+        // force the original to also be opaque
+        fPMColor16 |= (0xF << SK_A4444_SHIFT);
+    }
+}
+
+const SkBitmap* SkARGB4444_Blitter::justAnOpaqueColor(uint32_t* value) {
+    if (16 == fScale16) {
+        *value = fPMColor16;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+static void src_over_4444(SkPMColor16 dst[], SkPMColor16 color,
+                          SkPMColor16 other, unsigned invScale, int count) {
+    int twice = count >> 1;
+    while (--twice >= 0) {
+        *dst = color + SkAlphaMulQ4(*dst, invScale);
+        dst++;
+        *dst = other + SkAlphaMulQ4(*dst, invScale);
+        dst++;
+    }
+    if (count & 1) {
+        *dst = color + SkAlphaMulQ4(*dst, invScale);
+    }
+}
+
+static inline uint32_t SkExpand_4444_Replicate(SkPMColor16 c) {
+    uint32_t c32 = SkExpand_4444(c);
+    return c32 | (c32 << 4);
+}
+
+static void src_over_4444x(SkPMColor16 dst[], uint32_t color,
+                           uint32_t other, unsigned invScale, int count) {
+    int twice = count >> 1;
+    uint32_t tmp;
+    while (--twice >= 0) {
+        tmp = SkExpand_4444(*dst) * invScale;
+        *dst++ = SkCompact_4444((color + tmp) >> 4);
+        tmp = SkExpand_4444(*dst) * invScale;
+        *dst++ = SkCompact_4444((other + tmp) >> 4);
+    }
+    if (count & 1) {
+        tmp = SkExpand_4444(*dst) * invScale;
+        *dst = SkCompact_4444((color + tmp) >> 4);
+    }
+}
+
+void SkARGB4444_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+    if (0 == fScale16) {
+        return;
+    }
+
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+
+    if (16 == fScale16) {
+        sk_dither_memset16(device, color, other, width);
+    } else {
+        src_over_4444x(device, SkExpand_4444_Replicate(color),
+                       SkExpand_4444_Replicate(other),
+                       16 - fScale16, width);
+    }
+}
+
+void SkARGB4444_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    if (0 == alpha || 0 == fScale16) {
+        return;
+    }
+
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+    unsigned     rb = fDevice.rowBytes();
+
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+
+    if (16 == fScale16 && 255 == alpha) {
+        while (--height >= 0) {
+            *device = color;
+            device = (SkPMColor16*)((char*)device + rb);
+            SkTSwap<SkPMColor16>(color, other);
+        }
+    } else {
+        unsigned alphaScale = SkAlpha255To256(alpha);
+        uint32_t c32 = SkExpand_4444(color) * (alphaScale >> 4);
+        // need to normalize the low nibble of each expanded component
+        // so we don't overflow the add with d32
+        c32 = SkCompact_4444(c32 >> 4);
+        unsigned invScale = 16 - SkAlpha15To16(SkGetPackedA4444(c32));
+        // now re-expand and replicate
+        c32 = SkExpand_4444_Replicate(c32);
+
+        while (--height >= 0) {
+            uint32_t d32 = SkExpand_4444(*device) * invScale;
+            *device = SkCompact_4444((c32 + d32) >> 4);
+            device = (SkPMColor16*)((char*)device + rb);
+        }
+    }
+}
+
+void SkARGB4444_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() &&
+             y + height <= fDevice.height());
+
+    if (0 == fScale16) {
+        return;
+    }
+
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+
+    if (16 == fScale16) {
+        while (--height >= 0) {
+            sk_dither_memset16(device, color, other, width);
+            device = (SkPMColor16*)((char*)device + fDevice.rowBytes());
+            SkTSwap<SkPMColor16>(color, other);
+        }
+    } else {
+        unsigned invScale = 16 - fScale16;
+
+        uint32_t c32 = SkExpand_4444_Replicate(color);
+        uint32_t o32 = SkExpand_4444_Replicate(other);
+        while (--height >= 0) {
+            src_over_4444x(device, c32, o32, invScale, width);
+            device = (SkPMColor16*)((char*)device + fDevice.rowBytes());
+            SkTSwap<uint32_t>(c32, o32);
+        }
+    }
+}
+
+void SkARGB4444_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                   const int16_t runs[]) {
+    if (0 == fScale16) {
+        return;
+    }
+
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+
+        unsigned aa = antialias[0];
+        if (aa) {
+            if (0xFF == aa) {
+                if (16 == fScale16) {
+                    sk_dither_memset16(device, color, other, count);
+                } else {
+                    src_over_4444(device, color, other, 16 - fScale16, count);
+                }
+            } else {
+                // todo: respect dithering
+                aa = SkAlpha255To256(aa);   // FIX
+                SkPMColor16 src = SkAlphaMulQ4(color, aa >> 4);
+                unsigned dst_scale = SkAlpha15To16(15 - SkGetPackedA4444(src)); // FIX
+                int n = count;
+                do {
+                    --n;
+                    device[n] = src + SkAlphaMulQ4(device[n], dst_scale);
+                } while (n > 0);
+            }
+        }
+
+        runs += count;
+        antialias += count;
+        device += count;
+
+        if (count & 1) {
+            SkTSwap<SkPMColor16>(color, other);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define solid_8_pixels(mask, dst, color)    \
+    do {                                    \
+        if (mask & 0x80) dst[0] = color;    \
+        if (mask & 0x40) dst[1] = color;    \
+        if (mask & 0x20) dst[2] = color;    \
+        if (mask & 0x10) dst[3] = color;    \
+        if (mask & 0x08) dst[4] = color;    \
+        if (mask & 0x04) dst[5] = color;    \
+        if (mask & 0x02) dst[6] = color;    \
+        if (mask & 0x01) dst[7] = color;    \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB4444_BlitBW
+#define SK_BLITBWMASK_ARGS                  , SkPMColor16 color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+#define blend_8_pixels(mask, dst, sc, dst_scale)                             \
+    do {                                                                     \
+        if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ4(dst[0], dst_scale); }  \
+        if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ4(dst[1], dst_scale); }  \
+        if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ4(dst[2], dst_scale); }  \
+        if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ4(dst[3], dst_scale); }  \
+        if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ4(dst[4], dst_scale); }  \
+        if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ4(dst[5], dst_scale); }  \
+        if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ4(dst[6], dst_scale); }  \
+        if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ4(dst[7], dst_scale); }  \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB4444_BlendBW
+#define SK_BLITBWMASK_ARGS                  , uint16_t sc, unsigned dst_scale
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, sc, dst_scale)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkARGB4444_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+
+    if (0 == fScale16) {
+        return;
+    }
+
+    if (mask.fFormat == SkMask::kBW_Format) {
+        if (16 == fScale16) {
+            SkARGB4444_BlitBW(fDevice, mask, clip, fPMColor16);
+        } else {
+            SkARGB4444_BlendBW(fDevice, mask, clip, fPMColor16, 16 - fScale16);
+        }
+        return;
+    }
+
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+
+    SkPMColor16*    device = fDevice.getAddr16(x, y);
+    const uint8_t*  alpha = mask.getAddr8(x, y);
+    SkPMColor16     srcColor = fPMColor16;
+    unsigned        devRB = fDevice.rowBytes() - (width << 1);
+    unsigned        maskRB = mask.fRowBytes - width;
+
+    do {
+        int w = width;
+        do {
+            unsigned aa = *alpha++;
+            *device = SkBlendARGB4444(srcColor, *device, aa);
+            device += 1;
+        } while (--w != 0);
+        device = (SkPMColor16*)((char*)device + devRB);
+        alpha += maskRB;
+    } while (--height != 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkARGB4444_Shader_Blitter : public SkShaderBlitter {
+    SkXfermode*     fXfermode;
+    SkBlitRow::Proc fOpaqueProc;
+    SkBlitRow::Proc fAlphaProc;
+    SkPMColor*      fBuffer;
+    uint8_t*        fAAExpand;
+public:
+
+SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device, paint) {
+    const int width = device.width();
+    fBuffer = (SkPMColor*)sk_malloc_throw(width * sizeof(SkPMColor) + width);
+    fAAExpand = (uint8_t*)(fBuffer + width);
+
+    fXfermode = paint.getXfermode();
+    SkSafeRef(fXfermode);
+
+    unsigned flags = 0;
+    if (!(fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+        flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+    }
+    if (paint.isDither()) {
+        flags |= SkBlitRow::kDither_Flag;
+    }
+    fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kARGB_4444_Config);
+    fAlphaProc = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag,
+                                    SkBitmap::kARGB_4444_Config);
+}
+
+virtual ~SkARGB4444_Shader_Blitter() {
+    SkSafeUnref(fXfermode);
+    sk_free(fBuffer);
+}
+
+virtual void blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor*   span = fBuffer;
+
+    fShader->shadeSpan(x, y, span, width);
+    if (fXfermode) {
+        fXfermode->xfer4444(device, span, width, NULL);
+    } else {
+        fOpaqueProc(device, span, width, 0xFF, x, y);
+    }
+}
+
+virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
+                       const int16_t runs[]) {
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint8_t* SK_RESTRICT aaExpand = fAAExpand;
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkShader*    shader = fShader;
+    SkXfermode*  xfer = fXfermode;
+
+    if (NULL != xfer) {
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            int aa = *antialias;
+            if (aa) {
+                shader->shadeSpan(x, y, span, count);
+                if (255 == aa) {
+                    xfer->xfer4444(device, span, count, NULL);
+                } else {
+                    const uint8_t* aaBuffer = antialias;
+                    if (count > 1) {
+                        memset(aaExpand, aa, count);
+                        aaBuffer = aaExpand;
+                    }
+                    xfer->xfer4444(device, span, count, aaBuffer);
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    } else {    // no xfermode
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            int aa = *antialias;
+            if (aa) {
+                fShader->shadeSpan(x, y, span, count);
+                if (255 == aa) {
+                    fOpaqueProc(device, span, count, aa, x, y);
+                } else {
+                    fAlphaProc(device, span, count, aa, x, y);
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    }
+}
+
+private:
+    typedef SkShaderBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device,
+                                 const SkPaint& paint,
+                                 void* storage, size_t storageSize)
+{
+    SkBlitter* blitter;
+
+    if (paint.getShader()) {
+        SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Shader_Blitter, storage, storageSize, (device, paint));
+    } else {
+        SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Blitter, storage, storageSize, (device, paint));
+    }
+    return blitter;
+}
+
diff --git a/legacy/src/core/SkBlitter_A1.cpp b/legacy/src/core/SkBlitter_A1.cpp
new file mode 100644
index 0000000..5638477
--- /dev/null
+++ b/legacy/src/core/SkBlitter_A1.cpp
@@ -0,0 +1,51 @@
+
+/*
+ * 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 "SkCoreBlitters.h"
+
+SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device) {
+    fSrcA = paint.getAlpha();
+}
+
+void SkA1_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 &&
+             (unsigned)(x + width) <= (unsigned)fDevice.width());
+
+    if (fSrcA <= 0x7F) {
+        return;
+    }
+    uint8_t* dst = fDevice.getAddr1(x, y);
+    int right = x + width;
+
+    int left_mask = 0xFF >> (x & 7);
+    int rite_mask = 0xFF << (8 - (right & 7));
+    int full_runs = (right >> 3) - ((x + 7) >> 3);
+
+    // check for empty right mask, so we don't read off the end
+    // (or go slower than we need to)
+    if (rite_mask == 0) {
+        SkASSERT(full_runs >= 0);
+        full_runs -= 1;
+        rite_mask = 0xFF;
+    }
+    if (left_mask == 0xFF) {
+        full_runs -= 1;
+    }
+    if (full_runs < 0) {
+        SkASSERT((left_mask & rite_mask) != 0);
+        *dst |= (left_mask & rite_mask);
+    } else {
+        *dst++ |= left_mask;
+        memset(dst, 0xFF, full_runs);
+        dst += full_runs;
+        *dst |= rite_mask;
+    }
+}
+
diff --git a/legacy/src/core/SkBlitter_A8.cpp b/legacy/src/core/SkBlitter_A8.cpp
new file mode 100644
index 0000000..92a4971
--- /dev/null
+++ b/legacy/src/core/SkBlitter_A8.cpp
@@ -0,0 +1,346 @@
+
+/*
+ * 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 "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkXfermode.h"
+
+SkA8_Blitter::SkA8_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device) {
+    fSrcA = paint.getAlpha();
+}
+
+const SkBitmap* SkA8_Blitter::justAnOpaqueColor(uint32_t* value) {
+    if (255 == fSrcA) {
+        *value = 255;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+void SkA8_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 &&
+             (unsigned)(x + width) <= (unsigned)fDevice.width());
+
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint8_t* device = fDevice.getAddr8(x, y);
+
+    if (fSrcA == 255) {
+        memset(device, 0xFF, width);
+    } else {
+        unsigned scale = 256 - SkAlpha255To256(fSrcA);
+        unsigned srcA = fSrcA;
+
+        for (int i = 0; i < width; i++) {
+            device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
+        }
+    }
+}
+
+void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                             const int16_t runs[]) {
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint8_t*    device = fDevice.getAddr8(x, y);
+    unsigned    srcA = fSrcA;
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count == 0) {
+            return;
+        }
+        unsigned aa = antialias[0];
+
+        if (aa == 255 && srcA == 255) {
+            memset(device, 0xFF, count);
+        } else {
+            unsigned sa = SkAlphaMul(srcA, SkAlpha255To256(aa));
+            unsigned scale = 256 - sa;
+
+            for (int i = 0; i < count; i++) {
+                device[i] = SkToU8(sa + SkAlphaMul(device[i], scale));
+            }
+        }
+        runs += count;
+        antialias += count;
+        device += count;
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+#define solid_8_pixels(mask, dst)           \
+    do {                                    \
+        if (mask & 0x80) dst[0] = 0xFF;     \
+        if (mask & 0x40) dst[1] = 0xFF;     \
+        if (mask & 0x20) dst[2] = 0xFF;     \
+        if (mask & 0x10) dst[3] = 0xFF;     \
+        if (mask & 0x08) dst[4] = 0xFF;     \
+        if (mask & 0x04) dst[5] = 0xFF;     \
+        if (mask & 0x02) dst[6] = 0xFF;     \
+        if (mask & 0x01) dst[7] = 0xFF;     \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkA8_BlitBW
+#define SK_BLITBWMASK_ARGS
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst)
+#define SK_BLITBWMASK_GETADDR               getAddr8
+#define SK_BLITBWMASK_DEVTYPE               uint8_t
+#include "SkBlitBWMaskTemplate.h"
+
+static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa,
+                                  unsigned dst_scale) {
+    if (bw & 0x80) dst[0] = SkToU8(sa + SkAlphaMul(dst[0], dst_scale));
+    if (bw & 0x40) dst[1] = SkToU8(sa + SkAlphaMul(dst[1], dst_scale));
+    if (bw & 0x20) dst[2] = SkToU8(sa + SkAlphaMul(dst[2], dst_scale));
+    if (bw & 0x10) dst[3] = SkToU8(sa + SkAlphaMul(dst[3], dst_scale));
+    if (bw & 0x08) dst[4] = SkToU8(sa + SkAlphaMul(dst[4], dst_scale));
+    if (bw & 0x04) dst[5] = SkToU8(sa + SkAlphaMul(dst[5], dst_scale));
+    if (bw & 0x02) dst[6] = SkToU8(sa + SkAlphaMul(dst[6], dst_scale));
+    if (bw & 0x01) dst[7] = SkToU8(sa + SkAlphaMul(dst[7], dst_scale));
+}
+
+#define SK_BLITBWMASK_NAME                  SkA8_BlendBW
+#define SK_BLITBWMASK_ARGS                  , U8CPU sa, unsigned dst_scale
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, sa, dst_scale)
+#define SK_BLITBWMASK_GETADDR               getAddr8
+#define SK_BLITBWMASK_DEVTYPE               uint8_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    if (fSrcA == 0) {
+        return;
+    }
+
+    if (mask.fFormat == SkMask::kBW_Format) {
+        if (fSrcA == 0xFF) {
+            SkA8_BlitBW(fDevice, mask, clip);
+        } else {
+            SkA8_BlendBW(fDevice, mask, clip, fSrcA,
+                         SkAlpha255To256(255 - fSrcA));
+        }
+        return;
+    }
+
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+    uint8_t* device = fDevice.getAddr8(x, y);
+    const uint8_t* alpha = mask.getAddr8(x, y);
+    unsigned    srcA = fSrcA;
+
+    while (--height >= 0) {
+        for (int i = width - 1; i >= 0; --i) {
+            unsigned sa;
+            // scale our src by the alpha value
+            {
+                int aa = alpha[i];
+                if (aa == 0) {
+                    continue;
+                }
+                if (aa == 255) {
+                    if (srcA == 255) {
+                        device[i] = 0xFF;
+                        continue;
+                    }
+                    sa = srcA;
+                } else {
+                    sa = SkAlphaMul(srcA, SkAlpha255To256(aa));
+                }
+            }
+
+            int scale = 256 - SkAlpha255To256(sa);
+            device[i] = SkToU8(sa + SkAlphaMul(device[i], scale));
+        }
+        device += fDevice.rowBytes();
+        alpha += mask.fRowBytes;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    if (fSrcA == 0) {
+        return;
+    }
+
+    unsigned sa = SkAlphaMul(fSrcA, SkAlpha255To256(alpha));
+    uint8_t* device = fDevice.getAddr8(x, y);
+    int      rowBytes = fDevice.rowBytes();
+
+    if (sa == 0xFF) {
+        for (int i = 0; i < height; i++) {
+            *device = SkToU8(sa);
+            device += rowBytes;
+        }
+    } else {
+        unsigned scale = 256 - SkAlpha255To256(sa);
+
+        for (int i = 0; i < height; i++) {
+            *device = SkToU8(sa + SkAlphaMul(*device, scale));
+            device += rowBytes;
+        }
+    }
+}
+
+void SkA8_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x >= 0 && y >= 0 &&
+             (unsigned)(x + width) <= (unsigned)fDevice.width() &&
+             (unsigned)(y + height) <= (unsigned)fDevice.height());
+
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint8_t*    device = fDevice.getAddr8(x, y);
+    unsigned    srcA = fSrcA;
+
+    if (srcA == 255) {
+        while (--height >= 0) {
+            memset(device, 0xFF, width);
+            device += fDevice.rowBytes();
+        }
+    } else {
+        unsigned scale = 256 - SkAlpha255To256(srcA);
+
+        while (--height >= 0) {
+            for (int i = 0; i < width; i++) {
+                device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
+            }
+            device += fDevice.rowBytes();
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////
+
+SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device, paint) {
+    if ((fXfermode = paint.getXfermode()) != NULL) {
+        fXfermode->ref();
+        SkASSERT(fShader);
+    }
+
+    int width = device.width();
+    fBuffer = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * (width + (SkAlign4(width) >> 2)));
+    fAAExpand = (uint8_t*)(fBuffer + width);
+}
+
+SkA8_Shader_Blitter::~SkA8_Shader_Blitter() {
+    if (fXfermode) SkSafeUnref(fXfermode);
+    sk_free(fBuffer);
+}
+
+void SkA8_Shader_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 &&
+             (unsigned)(x + width) <= (unsigned)fDevice.width());
+
+    uint8_t* device = fDevice.getAddr8(x, y);
+
+    if ((fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) && !fXfermode) {
+        memset(device, 0xFF, width);
+    } else {
+        SkPMColor*  span = fBuffer;
+
+        fShader->shadeSpan(x, y, span, width);
+        if (fXfermode) {
+            fXfermode->xferA8(device, span, width, NULL);
+        } else {
+            for (int i = width - 1; i >= 0; --i) {
+                unsigned    srcA = SkGetPackedA32(span[i]);
+                unsigned    scale = 256 - SkAlpha255To256(srcA);
+
+                device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
+            }
+        }
+    }
+}
+
+static inline uint8_t aa_blend8(SkPMColor src, U8CPU da, int aa) {
+    SkASSERT((unsigned)aa <= 255);
+
+    int src_scale = SkAlpha255To256(aa);
+    int sa = SkGetPackedA32(src);
+    int dst_scale = 256 - SkAlphaMul(sa, src_scale);
+
+    return SkToU8((sa * src_scale + da * dst_scale) >> 8);
+}
+
+void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                    const int16_t runs[]) {
+    SkShader*   shader = fShader;
+    SkXfermode* mode = fXfermode;
+    uint8_t*    aaExpand = fAAExpand;
+    SkPMColor*  span = fBuffer;
+    uint8_t*    device = fDevice.getAddr8(x, y);
+    int         opaque = fShader->getFlags() & SkShader::kOpaqueAlpha_Flag;
+
+    for (;;) {
+        int count = *runs;
+        if (count == 0) {
+            break;
+        }
+        int aa = *antialias;
+        if (aa) {
+            if (opaque && aa == 255 && mode == NULL) {
+                memset(device, 0xFF, count);
+            } else {
+                shader->shadeSpan(x, y, span, count);
+                if (mode) {
+                    memset(aaExpand, aa, count);
+                    mode->xferA8(device, span, count, aaExpand);
+                } else {
+                    for (int i = count - 1; i >= 0; --i) {
+                        device[i] = aa_blend8(span[i], device[i], aa);
+                    }
+                }
+            }
+        }
+        device += count;
+        runs += count;
+        antialias += count;
+        x += count;
+    }
+}
+
+void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    if (mask.fFormat == SkMask::kBW_Format) {
+        this->INHERITED::blitMask(mask, clip);
+        return;
+    }
+
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+    uint8_t* device = fDevice.getAddr8(x, y);
+    const uint8_t* alpha = mask.getAddr8(x, y);
+
+    SkPMColor*  span = fBuffer;
+
+    while (--height >= 0) {
+        fShader->shadeSpan(x, y, span, width);
+        if (fXfermode) {
+            fXfermode->xferA8(device, span, width, alpha);
+        }
+
+        y += 1;
+        device += fDevice.rowBytes();
+        alpha += mask.fRowBytes;
+    }
+}
+
diff --git a/legacy/src/core/SkBlitter_ARGB32.cpp b/legacy/src/core/SkBlitter_ARGB32.cpp
new file mode 100644
index 0000000..24ab330
--- /dev/null
+++ b/legacy/src/core/SkBlitter_ARGB32.cpp
@@ -0,0 +1,431 @@
+/*
+ * 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 "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkBlitMask.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+
+    SkPMColor*		 dstRow = device.getAddr32(x, y);
+    const SkPMColor* srcRow = reinterpret_cast<const SkPMColor*>(mask.getAddr8(x, y));
+
+    do {
+		proc(dstRow, srcRow, width, alpha);
+        dstRow = (SkPMColor*)((char*)dstRow + device.rowBytes());
+        srcRow = (const SkPMColor*)((const char*)srcRow + mask.fRowBytes);
+    } while (--height != 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device) {
+    SkColor color = paint.getColor();
+    fColor = color;
+
+    fSrcA = SkColorGetA(color);
+    unsigned scale = SkAlpha255To256(fSrcA);
+    fSrcR = SkAlphaMul(SkColorGetR(color), scale);
+    fSrcG = SkAlphaMul(SkColorGetG(color), scale);
+    fSrcB = SkAlphaMul(SkColorGetB(color), scale);
+
+    fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB);
+    fColor32Proc = SkBlitRow::ColorProcFactory();
+}
+
+const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) {
+    if (255 == fSrcA) {
+        *value = fPMColor;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : local variable used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+void SkARGB32_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    fColor32Proc(device, device, width, fPMColor);
+}
+
+void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                 const int16_t runs[]) {
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint32_t    color = fPMColor;
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    unsigned    opaqueMask = fSrcA; // if fSrcA is 0xFF, then we will catch the fast opaque case
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        unsigned aa = antialias[0];
+        if (aa) {
+            if ((opaqueMask & aa) == 255) {
+                sk_memset32(device, color, count);
+            } else {
+                uint32_t sc = SkAlphaMulQ(color, SkAlpha255To256(aa));
+                fColor32Proc(device, device, count, sc);
+            }
+        }
+        runs += count;
+        antialias += count;
+        device += count;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#define solid_8_pixels(mask, dst, color)    \
+    do {                                    \
+        if (mask & 0x80) dst[0] = color;    \
+        if (mask & 0x40) dst[1] = color;    \
+        if (mask & 0x20) dst[2] = color;    \
+        if (mask & 0x10) dst[3] = color;    \
+        if (mask & 0x08) dst[4] = color;    \
+        if (mask & 0x04) dst[5] = color;    \
+        if (mask & 0x02) dst[6] = color;    \
+        if (mask & 0x01) dst[7] = color;    \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB32_BlitBW
+#define SK_BLITBWMASK_ARGS                  , SkPMColor color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR               getAddr32
+#define SK_BLITBWMASK_DEVTYPE               uint32_t
+#include "SkBlitBWMaskTemplate.h"
+
+#define blend_8_pixels(mask, dst, sc, dst_scale)                            \
+    do {                                                                    \
+        if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ(dst[0], dst_scale); }  \
+        if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ(dst[1], dst_scale); }  \
+        if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ(dst[2], dst_scale); }  \
+        if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ(dst[3], dst_scale); }  \
+        if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ(dst[4], dst_scale); }  \
+        if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ(dst[5], dst_scale); }  \
+        if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ(dst[6], dst_scale); }  \
+        if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ(dst[7], dst_scale); }  \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB32_BlendBW
+#define SK_BLITBWMASK_ARGS                  , uint32_t sc, unsigned dst_scale
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, sc, dst_scale)
+#define SK_BLITBWMASK_GETADDR               getAddr32
+#define SK_BLITBWMASK_DEVTYPE               uint32_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+    SkASSERT(fSrcA != 0xFF);
+
+    if (fSrcA == 0) {
+        return;
+    }
+
+    if (SkBlitMask::BlitColor(fDevice, mask, clip, fColor)) {
+        return;
+    }
+
+    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);
+    }
+}
+
+void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask,
+                                       const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+
+    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);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    if (alpha == 0 || fSrcA == 0) {
+        return;
+    }
+
+    uint32_t* device = fDevice.getAddr32(x, y);
+    uint32_t  color = fPMColor;
+
+    if (alpha != 255) {
+        color = SkAlphaMulQ(color, SkAlpha255To256(alpha));
+    }
+
+    unsigned dst_scale = 255 - SkGetPackedA32(color);
+    uint32_t rowBytes = fDevice.rowBytes();
+    while (--height >= 0) {
+        device[0] = color + SkAlphaMulQ(device[0], dst_scale);
+        device = (uint32_t*)((char*)device + rowBytes);
+    }
+}
+
+void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height());
+
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    uint32_t    color = fPMColor;
+    size_t      rowBytes = fDevice.rowBytes();
+
+    while (--height >= 0) {
+        fColor32Proc(device, device, width, color);
+        device = (uint32_t*)((char*)device + rowBytes);
+    }
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+///////////////////////////////////////////////////////////////////////
+
+void SkARGB32_Black_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                       const int16_t runs[]) {
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    SkPMColor   black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT);
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        unsigned aa = antialias[0];
+        if (aa) {
+            if (aa == 255) {
+                sk_memset32(device, black, count);
+            } else {
+                SkPMColor src = aa << SK_A32_SHIFT;
+                unsigned dst_scale = 256 - aa;
+                int n = count;
+                do {
+                    --n;
+                    device[n] = src + SkAlphaMulQ(device[n], dst_scale);
+                } while (n > 0);
+            }
+        }
+        runs += count;
+        antialias += count;
+        device += count;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device,
+                            const SkPaint& paint) : INHERITED(device, paint) {
+    fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor)));
+
+    fXfermode = paint.getXfermode();
+    SkSafeRef(fXfermode);
+
+    int flags = 0;
+    if (!(fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+        flags |= SkBlitRow::kSrcPixelAlpha_Flag32;
+    }
+    // we call this on the output from the shader
+    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);
+}
+
+SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() {
+    SkSafeUnref(fXfermode);
+    sk_free(fBuffer);
+}
+
+void SkARGB32_Shader_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+    uint32_t*   device = fDevice.getAddr32(x, y);
+
+    if (fXfermode == NULL && (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+        fShader->shadeSpan(x, y, device, width);
+    } else {
+        SkPMColor*  span = fBuffer;
+        fShader->shadeSpan(x, y, span, width);
+        if (fXfermode) {
+            fXfermode->xfer32(device, span, width, NULL);
+        } else {
+            fProc32(device, span, width, 255);
+        }
+    }
+}
+
+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) {
+        for (;;) {
+            SkXfermode* xfer = fXfermode;
+
+            int count = *runs;
+            if (count <= 0)
+                break;
+            int aa = *antialias;
+            if (aa) {
+                shader->shadeSpan(x, y, span, count);
+                if (aa == 255) {
+                    xfer->xfer32(device, span, count, NULL);
+                } else {
+                    // count is almost always 1
+                    for (int i = count - 1; i >= 0; --i) {
+                        xfer->xfer32(&device[i], &span[i], 1, antialias);
+                    }
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    } else if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) {
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            int aa = *antialias;
+            if (aa) {
+                if (aa == 255) {
+                    // cool, have the shader draw right into the device
+                    shader->shadeSpan(x, y, device, count);
+                } else {
+                    shader->shadeSpan(x, y, span, count);
+                    fProc32Blend(device, span, count, aa);
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    } else {    // no xfermode but the shader not opaque
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            int aa = *antialias;
+            if (aa) {
+                fShader->shadeSpan(x, y, span, count);
+                if (aa == 255) {
+                    fProc32(device, span, count, 255);
+                } else {
+                    fProc32Blend(device, span, count, aa);
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    }
+}
+
+void SkARGB32_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    // we only handle kA8 with an xfermode
+    if (fXfermode && (SkMask::kA8_Format != mask.fFormat)) {
+        this->INHERITED::blitMask(mask, clip);
+        return;
+    }
+
+    SkASSERT(mask.fBounds.contains(clip));
+
+    SkBlitMask::RowProc proc = NULL;
+    if (!fXfermode) {
+        unsigned flags = 0;
+        if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) {
+            flags |= SkBlitMask::kSrcIsOpaque_RowFlag;
+        }
+        proc = SkBlitMask::RowFactory(SkBitmap::kARGB_8888_Config, mask.fFormat,
+                                      (SkBlitMask::RowFlags)flags);
+        if (NULL == proc) {
+            this->INHERITED::blitMask(mask, clip);
+            return;
+        }
+    }
+
+    const int x = clip.fLeft;
+    const int width = clip.width();
+    int y = clip.fTop;
+    int height = clip.height();
+
+    char* dstRow = (char*)fDevice.getAddr32(x, y);
+    const size_t dstRB = fDevice.rowBytes();
+    const uint8_t* maskRow = (const uint8_t*)mask.getAddr(x, y);
+    const size_t maskRB = mask.fRowBytes;
+
+    SkShader* shader = fShader;
+    SkPMColor* span = fBuffer;
+
+    if (fXfermode) {
+        SkASSERT(SkMask::kA8_Format == mask.fFormat);
+        SkXfermode* xfer = fXfermode;
+        do {
+            shader->shadeSpan(x, y, span, width);
+            xfer->xfer32((SkPMColor*)dstRow, span, width, maskRow);
+            dstRow += dstRB;
+            maskRow += maskRB;
+            y += 1;
+        } while (--height > 0);
+    } else {
+        do {
+            shader->shadeSpan(x, y, span, width);
+            proc(dstRow, maskRow, span, width);
+            dstRow += dstRB;
+            maskRow += maskRB;
+            y += 1;
+        } while (--height > 0);
+    }
+}
+
diff --git a/legacy/src/core/SkBlitter_RGB16.cpp b/legacy/src/core/SkBlitter_RGB16.cpp
new file mode 100644
index 0000000..8a4d454
--- /dev/null
+++ b/legacy/src/core/SkBlitter_RGB16.cpp
@@ -0,0 +1,1065 @@
+
+/*
+ * 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 "SkBlitRow.h"
+#include "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkShader.h"
+#include "SkTemplatesPriv.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
+    #define SK_USE_NEON
+    #include <arm_neon.h>
+#else
+    // if we don't have neon, then our black blitter is worth the extra code
+    #define USE_BLACK_BLITTER
+#endif
+
+void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
+                        int count) {
+    if (count > 0) {
+        // see if we need to write one short before we can cast to an 4byte ptr
+        // (we do this subtract rather than (unsigned)dst so we don't get warnings
+        //  on 64bit machines)
+        if (((char*)dst - (char*)0) & 2) {
+            *dst++ = value;
+            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;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkRGB16_Blitter : public SkRasterBlitter {
+public:
+    SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint);
+    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 blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&,
+                          const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+    
+protected:
+    SkPMColor   fSrcColor32;
+    uint32_t    fExpandedRaw16;
+    unsigned    fScale;
+    uint16_t    fColor16;       // already scaled by fScale
+    uint16_t    fRawColor16;    // unscaled
+    uint16_t    fRawDither16;   // unscaled
+    SkBool8     fDoDither;
+    
+    // illegal
+    SkRGB16_Blitter& operator=(const SkRGB16_Blitter&);
+    
+    typedef SkRasterBlitter INHERITED;
+};
+
+class SkRGB16_Opaque_Blitter : public SkRGB16_Blitter {
+public:
+    SkRGB16_Opaque_Blitter(const SkBitmap& device, const SkPaint& paint);
+    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 blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&,
+                          const SkIRect&);
+    
+private:
+    typedef SkRGB16_Blitter INHERITED;
+};
+
+#ifdef USE_BLACK_BLITTER
+class SkRGB16_Black_Blitter : public SkRGB16_Opaque_Blitter {
+public:
+    SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint);
+    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;
+};
+#endif
+
+class SkRGB16_Shader_Blitter : public SkShaderBlitter {
+public:
+    SkRGB16_Shader_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkRGB16_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 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;
+};
+
+// used only if the shader can perform shadSpan16
+class SkRGB16_Shader16_Blitter : public SkRGB16_Shader_Blitter {
+public:
+    SkRGB16_Shader16_Blitter(const SkBitmap& device, const SkPaint& paint);
+    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 blitRect(int x, int y, int width, int height);
+    
+private:
+    typedef SkRGB16_Shader_Blitter INHERITED;
+};
+
+class SkRGB16_Shader_Xfermode_Blitter : public SkShaderBlitter {
+public:
+    SkRGB16_Shader_Xfermode_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkRGB16_Shader_Xfermode_Blitter();
+    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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef USE_BLACK_BLITTER
+SkRGB16_Black_Blitter::SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device, paint) {
+    SkASSERT(paint.getShader() == NULL);
+    SkASSERT(paint.getColorFilter() == NULL);
+    SkASSERT(paint.getXfermode() == NULL);
+    SkASSERT(paint.getColor() == SK_ColorBLACK);
+}
+
+#if 1
+#define black_8_pixels(mask, dst)       \
+    do {                                \
+        if (mask & 0x80) dst[0] = 0;    \
+        if (mask & 0x40) dst[1] = 0;    \
+        if (mask & 0x20) dst[2] = 0;    \
+        if (mask & 0x10) dst[3] = 0;    \
+        if (mask & 0x08) dst[4] = 0;    \
+        if (mask & 0x04) dst[5] = 0;    \
+        if (mask & 0x02) dst[6] = 0;    \
+        if (mask & 0x01) dst[7] = 0;    \
+    } while (0)
+#else
+static inline black_8_pixels(U8CPU mask, uint16_t dst[])
+{
+    if (mask & 0x80) dst[0] = 0;
+    if (mask & 0x40) dst[1] = 0;
+    if (mask & 0x20) dst[2] = 0;
+    if (mask & 0x10) dst[3] = 0;
+    if (mask & 0x08) dst[4] = 0;
+    if (mask & 0x04) dst[5] = 0;
+    if (mask & 0x02) dst[6] = 0;
+    if (mask & 0x01) dst[7] = 0;
+}
+#endif
+
+#define SK_BLITBWMASK_NAME                  SkRGB16_Black_BlitBW
+#define SK_BLITBWMASK_ARGS
+#define SK_BLITBWMASK_BLIT8(mask, dst)      black_8_pixels(mask, dst)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkRGB16_Black_Blitter::blitMask(const SkMask& mask,
+                                     const SkIRect& clip) {
+    if (mask.fFormat == SkMask::kBW_Format) {
+        SkRGB16_Black_BlitBW(fDevice, mask, clip);
+    } else {
+        uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop);
+        const uint8_t* SK_RESTRICT alpha = mask.getAddr8(clip.fLeft, clip.fTop);
+        unsigned width = clip.width();
+        unsigned height = clip.height();
+        unsigned deviceRB = fDevice.rowBytes() - (width << 1);
+        unsigned maskRB = mask.fRowBytes - width;
+
+        SkASSERT((int)height > 0);
+        SkASSERT((int)width > 0);
+        SkASSERT((int)deviceRB >= 0);
+        SkASSERT((int)maskRB >= 0);
+
+        do {
+            unsigned w = width;
+            do {
+                unsigned aa = *alpha++;
+                *device = SkAlphaMulRGB16(*device, SkAlpha255To256(255 - aa));
+                device += 1;
+            } while (--w != 0);
+            device = (uint16_t*)((char*)device + deviceRB);
+            alpha += maskRB;
+        } while (--height != 0);
+    }
+}
+
+void SkRGB16_Black_Blitter::blitAntiH(int x, int y,
+                                      const SkAlpha* SK_RESTRICT antialias,
+                                      const int16_t* SK_RESTRICT runs) {
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        runs += count;
+
+        unsigned aa = antialias[0];
+        antialias += count;
+        if (aa) {
+            if (aa == 255) {
+                memset(device, 0, count << 1);
+            } else {
+                aa = SkAlpha255To256(255 - aa);
+                do {
+                    *device = SkAlphaMulRGB16(*device, aa);
+                    device += 1;
+                } while (--count != 0);
+                continue;
+            }
+        }
+        device += count;
+    }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Opaque_Blitter::SkRGB16_Opaque_Blitter(const SkBitmap& device,
+                                               const SkPaint& paint)
+: INHERITED(device, paint) {}
+
+void SkRGB16_Opaque_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(width > 0);
+    SkASSERT(x + width <= fDevice.width());
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    uint16_t srcColor = fColor16;
+
+    SkASSERT(fRawColor16 == srcColor);
+    if (fDoDither) {
+        uint16_t ditherColor = fRawDither16;
+        if ((x ^ y) & 1) {
+            SkTSwap(ditherColor, srcColor);
+        }
+        sk_dither_memset16(device, srcColor, ditherColor, width);
+    } else {
+        sk_memset16(device, srcColor, width);
+    }
+}
+
+// return 1 or 0 from a bool
+static inline int Bool2Int(int value) {
+	return !!value;
+}
+
+void SkRGB16_Opaque_Blitter::blitAntiH(int x, int y,
+                                       const SkAlpha* SK_RESTRICT antialias,
+                                       const int16_t* SK_RESTRICT runs) {
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    uint16_t    srcColor = fRawColor16;
+    uint32_t    srcExpanded = fExpandedRaw16;
+    int         ditherInt = Bool2Int(fDoDither);
+    uint16_t    ditherColor = fRawDither16;
+    // if we have no dithering, this will always fail
+    if ((x ^ y) & ditherInt) {
+        SkTSwap(ditherColor, srcColor);
+    }
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        runs += count;
+
+        unsigned aa = antialias[0];
+        antialias += count;
+        if (aa) {
+            if (aa == 255) {
+                if (ditherInt) {
+                    sk_dither_memset16(device, srcColor,
+                                       ditherColor, count);
+                } else {
+                    sk_memset16(device, srcColor, count);
+                }
+            } else {
+                // TODO: respect fDoDither
+                unsigned scale5 = SkAlpha255To256(aa) >> 3;
+                uint32_t src32 = srcExpanded * scale5;
+                scale5 = 32 - scale5; // now we can use it on the device
+                int n = count;
+                do {
+                    uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+                    *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+                } while (--n != 0);
+                goto DONE;
+            }
+        }
+        device += count;
+
+    DONE:
+        // if we have no dithering, this will always fail
+        if (count & ditherInt) {
+            SkTSwap(ditherColor, srcColor);
+        }
+    }
+}
+
+#define solid_8_pixels(mask, dst, color)    \
+    do {                                    \
+        if (mask & 0x80) dst[0] = color;    \
+        if (mask & 0x40) dst[1] = color;    \
+        if (mask & 0x20) dst[2] = color;    \
+        if (mask & 0x10) dst[3] = color;    \
+        if (mask & 0x08) dst[4] = color;    \
+        if (mask & 0x04) dst[5] = color;    \
+        if (mask & 0x02) dst[6] = color;    \
+        if (mask & 0x01) dst[7] = color;    \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkRGB16_BlitBW
+#define SK_BLITBWMASK_ARGS                  , uint16_t color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+static U16CPU blend_compact(uint32_t src32, uint32_t dst32, unsigned scale5) {
+    return SkCompact_rgb_16(dst32 + ((src32 - dst32) * scale5 >> 5));
+}
+
+void SkRGB16_Opaque_Blitter::blitMask(const SkMask& mask,
+                                      const SkIRect& clip) {
+    if (mask.fFormat == SkMask::kBW_Format) {
+        SkRGB16_BlitBW(fDevice, mask, clip, fColor16);
+        return;
+    }
+
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop);
+    const uint8_t* SK_RESTRICT alpha = mask.getAddr8(clip.fLeft, clip.fTop);
+    int width = clip.width();
+    int height = clip.height();
+    unsigned    deviceRB = fDevice.rowBytes() - (width << 1);
+    unsigned    maskRB = mask.fRowBytes - width;
+    uint32_t    expanded32 = fExpandedRaw16;
+
+#ifdef SK_USE_NEON
+#define	UNROLL	8
+    do {
+        int w = width;
+        if (w >= UNROLL) {
+            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));
+                alpha_full = vaddq_u16(alpha_full, vshrq_n_u16(alpha_full,7));
+                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, 
+                                                         vdupq_n_u32(0x000007E0)),
+                                               16)
+                                   );
+                dev_hi = vorrq_u32(
+                                   vandq_u32(dev_hi, vdupq_n_u32(0x0000F81F)),
+                                   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),
+                                    SkAlpha255To256(*alpha++) >> 3);
+            device += 1;
+            --w;
+        }
+        device = (uint16_t*)((char*)device + deviceRB);
+        alpha += maskRB;
+    } while (--height != 0);
+#undef	UNROLL
+#else   // non-neon code
+    do {
+        int w = width;
+        do {
+            *device = blend_compact(expanded32, SkExpand_rgb_16(*device),
+                                    SkAlpha255To256(*alpha++) >> 3);
+            device += 1;
+        } while (--w != 0);
+        device = (uint16_t*)((char*)device + deviceRB);
+        alpha += maskRB;
+    } while (--height != 0);
+#endif
+}
+
+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;
+    scale5 = 32 - scale5;
+    do {
+        uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+        *device = SkCompact_rgb_16((src32 + dst32) >> 5);
+        device = (uint16_t*)((char*)device + deviceRB);
+    } while (--height != 0);
+}
+
+void SkRGB16_Opaque_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x + width <= fDevice.width() && y + height <= fDevice.height());
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    unsigned    deviceRB = fDevice.rowBytes();
+    uint16_t    color16 = fColor16;
+
+    if (fDoDither) {
+        uint16_t ditherColor = fRawDither16;
+        if ((x ^ y) & 1) {
+            SkTSwap(ditherColor, color16);
+        }
+        while (--height >= 0) {
+            sk_dither_memset16(device, color16, ditherColor, width);
+            SkTSwap(ditherColor, color16);
+            device = (uint16_t*)((char*)device + deviceRB);
+        }
+    } else {  // no dither
+        while (--height >= 0) {
+            sk_memset16(device, color16, width);
+            device = (uint16_t*)((char*)device + deviceRB);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Blitter::SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device) {
+    SkColor color = paint.getColor();
+
+    fSrcColor32 = SkPreMultiplyColor(color);
+    fScale = SkAlpha255To256(SkColorGetA(color));
+
+    int r = SkColorGetR(color);
+    int g = SkColorGetG(color);
+    int b = SkColorGetB(color);
+
+    fRawColor16 = fRawDither16 = SkPack888ToRGB16(r, g, b);
+    // if we're dithered, use fRawDither16 to hold that.
+    if ((fDoDither = paint.isDither()) != false) {
+        fRawDither16 = SkDitherPack888ToRGB16(r, g, b);
+    }
+
+    fExpandedRaw16 = SkExpand_rgb_16(fRawColor16);
+
+    fColor16 = SkPackRGB16( SkAlphaMul(r, fScale) >> (8 - SK_R16_BITS),
+                            SkAlphaMul(g, fScale) >> (8 - SK_G16_BITS),
+                            SkAlphaMul(b, fScale) >> (8 - SK_B16_BITS));
+}
+
+const SkBitmap* SkRGB16_Blitter::justAnOpaqueColor(uint32_t* value) {
+    if (!fDoDither && 256 == fScale) {
+        *value = fRawColor16;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+static uint32_t pmcolor_to_expand16(SkPMColor c) {
+    unsigned r = SkGetPackedR32(c);
+    unsigned g = SkGetPackedG32(c);
+    unsigned b = SkGetPackedB32(c);
+    return (g << 24) | (r << 13) | (b << 2);
+}
+
+static inline void blend32_16_row(SkPMColor src, uint16_t dst[], int count) {
+    SkASSERT(count > 0);
+    uint32_t src_expand = pmcolor_to_expand16(src);
+    unsigned scale = SkAlpha255To256(0xFF - SkGetPackedA32(src)) >> 3;
+    do {
+        uint32_t dst_expand = SkExpand_rgb_16(*dst) * scale;
+        *dst = SkCompact_rgb_16((src_expand + dst_expand) >> 5);
+        dst += 1;
+    } while (--count != 0);
+}
+
+void SkRGB16_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(width > 0);
+    SkASSERT(x + width <= fDevice.width());
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    // TODO: respect fDoDither
+    blend32_16_row(fSrcColor32, device, width);
+}
+
+void SkRGB16_Blitter::blitAntiH(int x, int y,
+                                const SkAlpha* SK_RESTRICT antialias,
+                                const int16_t* SK_RESTRICT runs) {
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    uint32_t    srcExpanded = fExpandedRaw16;
+    unsigned    scale = fScale;
+
+    // TODO: respect fDoDither
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        runs += count;
+
+        unsigned aa = antialias[0];
+        antialias += count;
+        if (aa) {
+            unsigned scale5 = SkAlpha255To256(aa) * scale >> (8 + 3);
+            uint32_t src32 =  srcExpanded * scale5;
+            scale5 = 32 - scale5;
+            do {
+                uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+                *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+            } while (--count != 0);
+            continue;
+        }
+        device += count;
+    }
+}
+
+static inline void blend_8_pixels(U8CPU bw, uint16_t dst[], unsigned dst_scale,
+                                  U16CPU srcColor) {
+    if (bw & 0x80) dst[0] = srcColor + SkAlphaMulRGB16(dst[0], dst_scale);
+    if (bw & 0x40) dst[1] = srcColor + SkAlphaMulRGB16(dst[1], dst_scale);
+    if (bw & 0x20) dst[2] = srcColor + SkAlphaMulRGB16(dst[2], dst_scale);
+    if (bw & 0x10) dst[3] = srcColor + SkAlphaMulRGB16(dst[3], dst_scale);
+    if (bw & 0x08) dst[4] = srcColor + SkAlphaMulRGB16(dst[4], dst_scale);
+    if (bw & 0x04) dst[5] = srcColor + SkAlphaMulRGB16(dst[5], dst_scale);
+    if (bw & 0x02) dst[6] = srcColor + SkAlphaMulRGB16(dst[6], dst_scale);
+    if (bw & 0x01) dst[7] = srcColor + SkAlphaMulRGB16(dst[7], dst_scale);
+}
+
+#define SK_BLITBWMASK_NAME                  SkRGB16_BlendBW
+#define SK_BLITBWMASK_ARGS                  , unsigned dst_scale, U16CPU src_color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, dst_scale, src_color)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkRGB16_Blitter::blitMask(const SkMask& mask,
+                               const SkIRect& clip) {
+    if (mask.fFormat == SkMask::kBW_Format) {
+        SkRGB16_BlendBW(fDevice, mask, clip, 256 - fScale, fColor16);
+        return;
+    }
+
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop);
+    const uint8_t* SK_RESTRICT alpha = mask.getAddr8(clip.fLeft, clip.fTop);
+    int width = clip.width();
+    int height = clip.height();
+    unsigned    deviceRB = fDevice.rowBytes() - (width << 1);
+    unsigned    maskRB = mask.fRowBytes - width;
+    uint32_t    color32 = fExpandedRaw16;
+
+    unsigned scale256 = fScale;
+    do {
+        int w = width;
+        do {
+            unsigned aa = *alpha++;
+            unsigned scale = SkAlpha255To256(aa) * scale256 >> (8 + 3);
+            uint32_t src32 = color32 * scale;
+            uint32_t dst32 = SkExpand_rgb_16(*device) * (32 - scale);
+            *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+        } while (--w != 0);
+        device = (uint16_t*)((char*)device + deviceRB);
+        alpha += maskRB;
+    } while (--height != 0);
+}
+
+void SkRGB16_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) * fScale >> (8 + 3);
+    uint32_t src32 =  fExpandedRaw16 * scale5;
+    scale5 = 32 - scale5;
+    do {
+        uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+        *device = SkCompact_rgb_16((src32 + dst32) >> 5);
+        device = (uint16_t*)((char*)device + deviceRB);
+    } while (--height != 0);
+}
+
+void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x + width <= fDevice.width() && y + height <= fDevice.height());
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    unsigned    deviceRB = fDevice.rowBytes();
+    SkPMColor src32 = fSrcColor32;
+
+    while (--height >= 0) {
+        blend32_16_row(src32, device, width);
+        device = (uint16_t*)((char*)device + deviceRB);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader16_Blitter::SkRGB16_Shader16_Blitter(const SkBitmap& device,
+                                                   const SkPaint& paint)
+    : SkRGB16_Shader_Blitter(device, paint) {
+    SkASSERT(SkShader::CanCallShadeSpan16(fShaderFlags));
+}
+
+void SkRGB16_Shader16_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x + width <= fDevice.width());
+
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    SkShader*   shader = fShader;
+
+    int alpha = shader->getSpan16Alpha();
+    if (0xFF == alpha) {
+        shader->shadeSpan16(x, y, device, width);
+    } else {
+        uint16_t* span16 = (uint16_t*)fBuffer;
+        shader->shadeSpan16(x, y, span16, width);
+        SkBlendRGB16(span16, device, SkAlpha255To256(alpha), width);
+    }
+}
+
+void SkRGB16_Shader16_Blitter::blitRect(int x, int y, int width, int height) {
+    SkShader*   shader = fShader;
+    uint16_t*   dst = fDevice.getAddr16(x, y);
+    size_t      dstRB = fDevice.rowBytes();
+    int         alpha = shader->getSpan16Alpha();
+
+    if (0xFF == alpha) {
+        if (fShaderFlags & SkShader::kConstInY16_Flag) {
+            // have the shader blit directly into the device the first time
+            shader->shadeSpan16(x, y, dst, width);
+            // and now just memcpy that line on the subsequent lines
+            if (--height > 0) {
+                const uint16_t* orig = dst;
+                do {
+                    dst = (uint16_t*)((char*)dst + dstRB);
+                    memcpy(dst, orig, width << 1);
+                } while (--height);
+            }
+        } else {    // need to call shadeSpan16 for every line
+            do {
+                shader->shadeSpan16(x, y, dst, width);
+                y += 1;
+                dst = (uint16_t*)((char*)dst + dstRB);
+            } while (--height);
+        }
+    } else {
+        int scale = SkAlpha255To256(alpha);
+        uint16_t* span16 = (uint16_t*)fBuffer;
+        if (fShaderFlags & SkShader::kConstInY16_Flag) {
+            shader->shadeSpan16(x, y, span16, width);
+            do {
+                SkBlendRGB16(span16, dst, scale, width);
+                dst = (uint16_t*)((char*)dst + dstRB);
+            } while (--height);
+        } else {
+            do {
+                shader->shadeSpan16(x, y, span16, width);
+                SkBlendRGB16(span16, dst, scale, width);
+                y += 1;
+                dst = (uint16_t*)((char*)dst + dstRB);
+            } while (--height);
+        }
+    }
+}
+
+void SkRGB16_Shader16_Blitter::blitAntiH(int x, int y,
+                                         const SkAlpha* SK_RESTRICT antialias,
+                                         const int16_t* SK_RESTRICT runs) {
+    SkShader*   shader = fShader;
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    int alpha = shader->getSpan16Alpha();
+    uint16_t* span16 = (uint16_t*)span;
+
+    if (0xFF == alpha) {
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            SkASSERT(count <= fDevice.width()); // don't overrun fBuffer
+
+            int aa = *antialias;
+            if (aa == 255) {
+                // go direct to the device!
+                shader->shadeSpan16(x, y, device, count);
+            } else if (aa) {
+                shader->shadeSpan16(x, y, span16, count);
+                SkBlendRGB16(span16, device, SkAlpha255To256(aa), count);
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    } else {  // span alpha is < 255
+        alpha = SkAlpha255To256(alpha);
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            SkASSERT(count <= fDevice.width()); // don't overrun fBuffer
+
+            int aa = SkAlphaMul(*antialias, alpha);
+            if (aa) {
+                shader->shadeSpan16(x, y, span16, count);
+                SkBlendRGB16(span16, device, SkAlpha255To256(aa), count);
+            }
+
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkBitmap& device,
+                                               const SkPaint& paint)
+: INHERITED(device, paint) {
+    SkASSERT(paint.getXfermode() == NULL);
+
+    fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor));
+
+    // 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)) {
+        flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+        }
+    // don't dither if the shader is really 16bit
+    if (paint.isDither() && !(shaderFlags & SkShader::kIntrinsicly16_Flag)) {
+        flags |= SkBlitRow::kDither_Flag;
+    }
+    // used when we know our global alpha is 0xFF
+    fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config);
+    // used when we know our global alpha is < 0xFF
+    fAlphaProc  = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag,
+                                     SkBitmap::kRGB_565_Config);
+}
+
+SkRGB16_Shader_Blitter::~SkRGB16_Shader_Blitter() {
+    sk_free(fBuffer);
+}
+
+void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x + width <= fDevice.width());
+
+    fShader->shadeSpan(x, y, fBuffer, width);
+    // shaders take care of global alpha, so we pass 0xFF (should be ignored)
+    fOpaqueProc(fDevice.getAddr16(x, y), fBuffer, width, 0xFF, x, y);
+}
+
+void SkRGB16_Shader_Blitter::blitRect(int x, int y, int width, int height) {
+    SkShader*       shader = fShader;
+    SkBlitRow::Proc proc = fOpaqueProc;
+    SkPMColor*      buffer = fBuffer;
+    uint16_t*       dst = fDevice.getAddr16(x, y);
+    size_t          dstRB = fDevice.rowBytes();
+
+    if (fShaderFlags & SkShader::kConstInY32_Flag) {
+        shader->shadeSpan(x, y, buffer, width);
+        do {
+            proc(dst, buffer, width, 0xFF, x, y);
+            y += 1;
+            dst = (uint16_t*)((char*)dst + dstRB);
+        } while (--height);
+    } else {
+        do {
+            shader->shadeSpan(x, y, buffer, width);
+            proc(dst, buffer, width, 0xFF, x, y);
+            y += 1;
+            dst = (uint16_t*)((char*)dst + dstRB);
+        } while (--height);
+    }
+}
+
+static inline int count_nonzero_span(const int16_t runs[], const SkAlpha aa[]) {
+    int count = 0;
+    for (;;) {
+        int n = *runs;
+        if (n == 0 || *aa == 0) {
+            break;
+        }
+        runs += n;
+        aa += n;
+        count += n;
+    }
+    return count;
+}
+
+void SkRGB16_Shader_Blitter::blitAntiH(int x, int y,
+                                       const SkAlpha* SK_RESTRICT antialias,
+                                       const int16_t* SK_RESTRICT runs) {
+    SkShader*   shader = fShader;
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    for (;;) {
+        int count = *runs;
+        if (count <= 0) {
+            break;
+        }
+        int aa = *antialias;
+        if (0 == aa) {
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+            continue;
+        }
+
+        int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count);
+
+        SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
+        shader->shadeSpan(x, y, span, nonZeroCount);
+
+        SkPMColor* localSpan = span;
+        for (;;) {
+            SkBlitRow::Proc proc = (aa == 0xFF) ? fOpaqueProc : fAlphaProc;
+            proc(device, localSpan, count, aa, x, y);
+
+            x += count;
+            device += count;
+            runs += count;
+            antialias += count;
+            nonZeroCount -= count;
+            if (nonZeroCount == 0) {
+                break;
+            }
+            localSpan += count;
+            SkASSERT(nonZeroCount > 0);
+            count = *runs;
+            SkASSERT(count > 0);
+            aa = *antialias;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader_Xfermode_Blitter::SkRGB16_Shader_Xfermode_Blitter(
+                                const SkBitmap& device, const SkPaint& paint)
+: INHERITED(device, paint) {
+    fXfermode = paint.getXfermode();
+    SkASSERT(fXfermode);
+    fXfermode->ref();
+
+    int width = device.width();
+    fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor));
+    fAAExpand = (uint8_t*)(fBuffer + width);
+}
+
+SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() {
+    fXfermode->unref();
+    sk_free(fBuffer);
+}
+
+void SkRGB16_Shader_Xfermode_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x + width <= fDevice.width());
+
+    uint16_t*   device = fDevice.getAddr16(x, y);
+    SkPMColor*  span = fBuffer;
+
+    fShader->shadeSpan(x, y, span, width);
+    fXfermode->xfer16(device, span, width, NULL);
+}
+
+void SkRGB16_Shader_Xfermode_Blitter::blitAntiH(int x, int y,
+                                const SkAlpha* SK_RESTRICT antialias,
+                                const int16_t* SK_RESTRICT runs) {
+    SkShader*   shader = fShader;
+    SkXfermode* mode = fXfermode;
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint8_t* SK_RESTRICT aaExpand = fAAExpand;
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    for (;;) {
+        int count = *runs;
+        if (count <= 0) {
+            break;
+        }
+        int aa = *antialias;
+        if (0 == aa) {
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+            continue;
+        }
+
+        int nonZeroCount = count + count_nonzero_span(runs + count,
+                                                      antialias + count);
+
+        SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
+        shader->shadeSpan(x, y, span, nonZeroCount);
+
+        x += nonZeroCount;
+        SkPMColor* localSpan = span;
+        for (;;) {
+            if (aa == 0xFF) {
+                mode->xfer16(device, localSpan, count, NULL);
+            } else {
+                SkASSERT(aa);
+                memset(aaExpand, aa, count);
+                mode->xfer16(device, localSpan, count, aaExpand);
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            nonZeroCount -= count;
+            if (nonZeroCount == 0) {
+                break;
+            }
+            localSpan += count;
+            SkASSERT(nonZeroCount > 0);
+            count = *runs;
+            SkASSERT(count > 0);
+            aa = *antialias;
+        }
+    } 
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlitter* SkBlitter_ChooseD565(const SkBitmap& device, const SkPaint& paint,
+                                void* storage, size_t storageSize) {
+    SkBlitter* blitter;
+    SkShader* shader = paint.getShader();
+    SkXfermode* mode = paint.getXfermode();
+
+    // we require a shader if there is an xfermode, handled by our caller
+    SkASSERT(NULL == mode || NULL != shader);
+
+    if (shader) {
+        if (mode) {
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Xfermode_Blitter,
+                                  storage, storageSize, (device, paint));
+        } else if (shader->canCallShadeSpan16()) {
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader16_Blitter,
+                                  storage, storageSize, (device, paint));
+        } else {
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Blitter,
+                                  storage, storageSize, (device, paint));
+        }
+    } else {
+        // no shader, no xfermode, (and we always ignore colorfilter)
+        SkColor color = paint.getColor();
+        if (0 == SkColorGetA(color)) {
+            SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+#ifdef USE_BLACK_BLITTER
+        } else if (SK_ColorBLACK == color) {
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Black_Blitter, storage,
+                                  storageSize, (device, paint));
+#endif
+        } else if (0xFF == SkColorGetA(color)) {
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Opaque_Blitter, storage,
+                                  storageSize, (device, paint));
+        } else {
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Blitter, storage,
+                                  storageSize, (device, paint));
+        }
+    }
+    
+    return blitter;
+}
diff --git a/legacy/src/core/SkBlitter_Sprite.cpp b/legacy/src/core/SkBlitter_Sprite.cpp
new file mode 100644
index 0000000..c2a0062
--- /dev/null
+++ b/legacy/src/core/SkBlitter_Sprite.cpp
@@ -0,0 +1,88 @@
+
+/*
+ * 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 "SkSpriteBlitter.h"
+
+SkSpriteBlitter::SkSpriteBlitter(const SkBitmap& source)
+        : fSource(&source) {
+    fSource->lockPixels();
+}
+
+SkSpriteBlitter::~SkSpriteBlitter() {
+    fSource->unlockPixels();
+}
+
+void SkSpriteBlitter::setup(const SkBitmap& device, int left, int top,
+                            const SkPaint& paint) {
+    fDevice = &device;
+    fLeft = left;
+    fTop = top;
+    fPaint = &paint;
+}
+
+#ifdef SK_DEBUG
+void SkSpriteBlitter::blitH(int x, int y, int width) {
+    SkDEBUGFAIL("how did we get here?");
+}
+
+void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                const int16_t runs[]) {
+    SkDEBUGFAIL("how did we get here?");
+}
+
+void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    SkDEBUGFAIL("how did we get here?");
+}
+
+void SkSpriteBlitter::blitMask(const SkMask&, const SkIRect& clip) {
+    SkDEBUGFAIL("how did we get here?");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// returning null means the caller will call SkBlitter::Choose() and
+// have wrapped the source bitmap inside a shader
+SkBlitter* SkBlitter::ChooseSprite( const SkBitmap& device,
+                                    const SkPaint& paint,
+                                    const SkBitmap& source,
+                                    int left, int top,
+                                    void* storage, size_t storageSize) {
+    /*  We currently ignore antialiasing and filtertype, meaning we will take our
+        special blitters regardless of these settings. Ignoring filtertype seems fine
+        since by definition there is no scale in the matrix. Ignoring antialiasing is
+        a bit of a hack, since we "could" pass in the fractional left/top for the bitmap,
+        and respect that by blending the edges of the bitmap against the device. To support
+        this we could either add more special blitters here, or detect antialiasing in the
+        paint and return null if it is set, forcing the client to take the slow shader case
+        (which does respect soft edges).
+    */
+
+    SkSpriteBlitter* blitter;
+
+    switch (device.getConfig()) {
+        case SkBitmap::kRGB_565_Config:
+            blitter = SkSpriteBlitter::ChooseD16(source, paint, storage,
+                                                 storageSize);
+            break;
+        case SkBitmap::kARGB_8888_Config:
+            blitter = SkSpriteBlitter::ChooseD32(source, paint, storage,
+                                                 storageSize);
+            break;
+        default:
+            blitter = NULL;
+            break;
+    }
+
+    if (blitter) {
+        blitter->setup(device, left, top, paint);
+    }
+    return blitter;
+}
+
diff --git a/legacy/src/core/SkBuffer.cpp b/legacy/src/core/SkBuffer.cpp
new file mode 100644
index 0000000..915264d
--- /dev/null
+++ b/legacy/src/core/SkBuffer.cpp
@@ -0,0 +1,129 @@
+
+/*
+ * 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 "SkBuffer.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void SkRBuffer::readNoSizeCheck(void* buffer, size_t size)
+{
+    SkASSERT((fData != 0 && fStop == 0) || fPos + size <= fStop);
+    if (buffer)
+        memcpy(buffer, fPos, size);
+    fPos += size;
+}
+
+const void* SkRBuffer::skip(size_t size)
+{
+    const void* result = fPos;
+    readNoSizeCheck(NULL, size);
+    return result;
+}
+
+size_t SkRBuffer::skipToAlign4()
+{
+    size_t pos = this->pos();
+    size_t n = SkAlign4(pos) - pos;
+    fPos += n;
+    return n;
+}
+
+void* SkWBuffer::skip(size_t size)
+{
+    void* result = fPos;
+    writeNoSizeCheck(NULL, size);
+    return fData == NULL ? NULL : result;
+}
+
+void SkWBuffer::writeNoSizeCheck(const void* buffer, size_t size)
+{
+    SkASSERT(fData == 0 || fStop == 0 || fPos + size <= fStop);
+    if (fData && buffer)
+        memcpy(fPos, buffer, size);
+    fPos += size;
+}
+
+size_t SkWBuffer::padToAlign4()
+{
+    size_t pos = this->pos();
+    size_t n = SkAlign4(pos) - pos;
+
+    if (n && fData)
+    {
+        char* p = fPos;
+        char* stop = p + n;
+        do {
+            *p++ = 0;
+        } while (p < stop);
+    }
+    fPos += n;
+    return n;
+}
+
+#if 0
+#ifdef SK_DEBUG
+    static void AssertBuffer32(const void* buffer)
+    {
+        SkASSERT(buffer);
+        SkASSERT(((size_t)buffer & 3) == 0);
+    }
+#else
+    #define AssertBuffer32(buffer)
+#endif
+
+void* sk_buffer_write_int32(void* buffer, int32_t value)
+{
+    AssertBuffer32(buffer);
+    *(int32_t*)buffer = value;
+    return (char*)buffer + sizeof(int32_t);
+}
+
+void* sk_buffer_write_int32(void* buffer, const int32_t values[], int count)
+{
+    AssertBuffer32(buffer);
+    SkASSERT(count >= 0);
+
+    memcpy((int32_t*)buffer, values, count * sizeof(int32_t));
+    return (char*)buffer + count * sizeof(int32_t);
+}
+
+const void* sk_buffer_read_int32(const void* buffer, int32_t* value)
+{
+    AssertBuffer32(buffer);
+    if (value)
+        *value = *(const int32_t*)buffer;
+    return (const char*)buffer + sizeof(int32_t);
+}
+
+const void* sk_buffer_read_int32(const void* buffer, int32_t values[], int count)
+{
+    AssertBuffer32(buffer);
+    SkASSERT(count >= 0);
+
+    if (values)
+        memcpy(values, (const int32_t*)buffer, count * sizeof(int32_t));
+    return (const char*)buffer + count * sizeof(int32_t);
+}
+
+void* sk_buffer_write_ptr(void* buffer, void* ptr)
+{
+    AssertBuffer32(buffer);
+    *(void**)buffer = ptr;
+    return (char*)buffer + sizeof(void*);
+}
+
+const void* sk_buffer_read_ptr(const void* buffer, void** ptr)
+{
+    AssertBuffer32(buffer);
+    if (ptr)
+        *ptr = *(void**)buffer;
+    return (const char*)buffer + sizeof(void*);
+}
+
+#endif
diff --git a/legacy/src/core/SkCanvas.cpp b/legacy/src/core/SkCanvas.cpp
new file mode 100644
index 0000000..0e26728
--- /dev/null
+++ b/legacy/src/core/SkCanvas.cpp
@@ -0,0 +1,1980 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkCanvas.h"
+#include "SkBounder.h"
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawLooper.h"
+#include "SkPicture.h"
+#include "SkRasterClip.h"
+#include "SkScalarCompare.h"
+#include "SkTemplates.h"
+#include "SkTextFormatParams.h"
+#include "SkTLazy.h"
+#include "SkUtils.h"
+
+//#define SK_TRACE_SAVERESTORE
+
+#ifdef SK_TRACE_SAVERESTORE
+    static int gLayerCounter;
+    static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
+    static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
+
+    static int gRecCounter;
+    static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
+    static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
+
+    static int gCanvasCounter;
+    static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
+    static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
+#else
+    #define inc_layer()
+    #define dec_layer()
+    #define inc_rec()
+    #define dec_rec()
+    #define inc_canvas()
+    #define dec_canvas()
+#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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  This is the record we keep for each SkDevice that the user installs.
+    The clip/matrix/proc are fields that reflect the top of the save/restore
+    stack. Whenever the canvas changes, it marks a dirty flag, and then before
+    these are used (assuming we're not on a layer) we rebuild these cache
+    values: they reflect the top of the save stack, but translated and clipped
+    by the device's XY offset and bitmap-bounds.
+*/
+struct DeviceCM {
+    DeviceCM*           fNext;
+    SkDevice*           fDevice;
+    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)
+            : fNext(NULL) {
+        if (NULL != device) {
+            device->ref();
+            device->lockPixels();
+        }
+        fDevice = device;
+        fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
+    }
+
+    ~DeviceCM() {
+        if (NULL != fDevice) {
+            fDevice->unlockPixels();
+            fDevice->unref();
+        }
+        SkDELETE(fPaint);
+    }
+
+    void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
+                  const SkClipStack& clipStack, SkRasterClip* updateClip) {
+        int x = fDevice->getOrigin().x();
+        int y = fDevice->getOrigin().y();
+        int width = fDevice->width();
+        int height = fDevice->height();
+
+        if ((x | y) == 0) {
+            fMatrix = &totalMatrix;
+            fClip = totalClip;
+        } else {
+            fMatrixStorage = totalMatrix;
+            fMatrixStorage.postTranslate(SkIntToScalar(-x),
+                                         SkIntToScalar(-y));
+            fMatrix = &fMatrixStorage;
+
+            totalClip.translate(-x, -y, &fClip);
+        }
+
+        fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
+
+        // intersect clip, but don't translate it (yet)
+
+        if (updateClip) {
+            updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
+                           SkRegion::kDifference_Op);
+        }
+
+        fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
+
+#ifdef SK_DEBUG
+        if (!fClip.isEmpty()) {
+            SkIRect deviceR;
+            deviceR.set(0, 0, width, height);
+            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;
+};
+
+/*  This is the record we keep for each save/restore level in the stack.
+    Since a level optionally copies the matrix and/or stack, we have pointers
+    for these fields. If the value is copied for this level, the copy is
+    stored in the ...Storage field, and the pointer points to that. If the
+    value is not copied for this level, we ignore ...Storage, and just point
+    at the corresponding value in the previous level in the stack.
+*/
+class SkCanvas::MCRec {
+public:
+    MCRec*          fNext;
+    SkMatrix*       fMatrix;        // points to either fMatrixStorage or prev MCRec
+    SkRasterClip*   fRasterClip;    // points to either fRegionStorage or prev MCRec
+    SkDrawFilter*   fFilter;        // the current filter (or null)
+
+    DeviceCM*   fLayer;
+    /*  If there are any layers in the stack, this points to the top-most
+        one that is at or below this level in the stack (so we know what
+        bitmap/device to draw into from this level. This value is NOT
+        reference counted, since the real owner is either our fLayer field,
+        or a previous one in a lower level.)
+    */
+    DeviceCM*   fTopLayer;
+
+    MCRec(const MCRec* prev, int flags) {
+        if (NULL != prev) {
+            if (flags & SkCanvas::kMatrix_SaveFlag) {
+                fMatrixStorage = *prev->fMatrix;
+                fMatrix = &fMatrixStorage;
+            } else {
+                fMatrix = prev->fMatrix;
+            }
+
+            if (flags & SkCanvas::kClip_SaveFlag) {
+                fRasterClipStorage = *prev->fRasterClip;
+                fRasterClip = &fRasterClipStorage;
+            } else {
+                fRasterClip = prev->fRasterClip;
+            }
+
+            fFilter = prev->fFilter;
+            SkSafeRef(fFilter);
+
+            fTopLayer = prev->fTopLayer;
+        } else {   // no prev
+            fMatrixStorage.reset();
+
+            fMatrix     = &fMatrixStorage;
+            fRasterClip = &fRasterClipStorage;
+            fFilter     = NULL;
+            fTopLayer   = NULL;
+        }
+        fLayer = NULL;
+
+        // don't bother initializing fNext
+        inc_rec();
+    }
+    ~MCRec() {
+        SkSafeUnref(fFilter);
+        SkDELETE(fLayer);
+        dec_rec();
+    }
+
+private:
+    SkMatrix        fMatrixStorage;
+    SkRasterClip    fRasterClipStorage;
+};
+
+class SkDrawIter : public SkDraw {
+public:
+    SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
+        canvas = canvas->canvasForDrawIter();
+        fCanvas = canvas;
+        canvas->updateDeviceCMCache();
+
+        fClipStack = &canvas->getTotalClipStack();
+        fBounder = canvas->getBounder();
+        fCurrLayer = canvas->fMCRec->fTopLayer;
+        fSkipEmptyClips = skipEmptyClips;
+    }
+
+    bool next() {
+        // skip over recs with empty clips
+        if (fSkipEmptyClips) {
+            while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
+                fCurrLayer = fCurrLayer->fNext;
+            }
+        }
+
+        const DeviceCM* rec = fCurrLayer;
+        if (rec && rec->fDevice) {
+
+            fMatrix = rec->fMatrix;
+            fClip   = &((SkRasterClip*)&rec->fClip)->forceGetBW();
+            fRC     = &rec->fClip;
+            fDevice = rec->fDevice;
+            fBitmap = &fDevice->accessBitmap(true);
+            fPaint  = rec->fPaint;
+            fMVMatrix = rec->fMVMatrix;
+            fExtMatrix = rec->fExtMatrix;
+            SkDEBUGCODE(this->validate();)
+
+            fCurrLayer = rec->fNext;
+            if (fBounder) {
+                fBounder->setClip(fClip);
+            }
+            // fCurrLayer may be NULL now
+
+            fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
+            return true;
+        }
+        return false;
+    }
+
+    SkDevice* getDevice() const { return fDevice; }
+    int getX() const { return fDevice->getOrigin().x(); }
+    int getY() const { return fDevice->getOrigin().y(); }
+    const SkMatrix& getMatrix() const { return *fMatrix; }
+    const SkRegion& getClip() const { return *fClip; }
+    const SkPaint* getPaint() const { return fPaint; }
+
+private:
+    SkCanvas*       fCanvas;
+    const DeviceCM* fCurrLayer;
+    const SkPaint*  fPaint;     // May be null.
+    SkBool8         fSkipEmptyClips;
+
+    typedef SkDraw INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class AutoDrawLooper {
+public:
+    AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
+        fCanvas = canvas;
+        fLooper = paint.getLooper();
+        fFilter = canvas->getDrawFilter();
+        fPaint = NULL;
+        fSaveCount = canvas->getSaveCount();
+        fDone = false;
+
+        if (fLooper) {
+            fLooper->init(canvas);
+        }
+    }
+
+    ~AutoDrawLooper() {
+        SkASSERT(fCanvas->getSaveCount() == fSaveCount);
+    }
+
+    const SkPaint& paint() const {
+        SkASSERT(fPaint);
+        return *fPaint;
+    }
+
+    bool next(SkDrawFilter::Type drawType);
+
+private:
+    SkLazyPaint     fLazyPaint;
+    SkCanvas*       fCanvas;
+    const SkPaint&  fOrigPaint;
+    SkDrawLooper*   fLooper;
+    SkDrawFilter*   fFilter;
+    const SkPaint*  fPaint;
+    int             fSaveCount;
+    bool            fDone;
+};
+
+bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
+    fPaint = NULL;
+    if (fDone) {
+        return false;
+    }
+
+    if (fLooper || fFilter) {
+        SkPaint* paint = fLazyPaint.set(fOrigPaint);
+        if (fLooper && !fLooper->next(fCanvas, paint)) {
+            fDone = true;
+            return false;
+        }
+        if (fFilter) {
+            fFilter->filter(paint, drawType);
+            if (NULL == fLooper) {
+                // no looper means we only draw once
+                fDone = true;
+            }
+        }
+        fPaint = paint;
+    } else {
+        fDone = true;
+        fPaint = &fOrigPaint;
+    }
+
+    // call this after any possible paint modifiers
+    if (fPaint->nothingToDraw()) {
+        fPaint = NULL;
+        return false;
+    }
+    return true;
+}
+
+/*  Stack helper for managing a SkBounder. In the destructor, if we were
+    given a bounder, we call its commit() method, signifying that we are
+    done accumulating bounds for that draw.
+*/
+class SkAutoBounderCommit {
+public:
+    SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
+    ~SkAutoBounderCommit() {
+        if (NULL != fBounder) {
+            fBounder->commit();
+        }
+    }
+private:
+    SkBounder*  fBounder;
+};
+
+#include "SkColorPriv.h"
+
+class AutoValidator {
+public:
+    AutoValidator(SkDevice* device) : fDevice(device) {}
+    ~AutoValidator() {
+#ifdef SK_DEBUG
+        const SkBitmap& bm = fDevice->accessBitmap(false);
+        if (bm.config() == SkBitmap::kARGB_4444_Config) {
+            for (int y = 0; y < bm.height(); y++) {
+                const SkPMColor16* p = bm.getAddr16(0, y);
+                for (int x = 0; x < bm.width(); x++) {
+                    SkPMColor16 c = p[x];
+                    SkPMColor16Assert(c);
+                }
+            }
+        }
+#endif
+    }
+private:
+    SkDevice* fDevice;
+};
+
+////////// macros to place around the internal draw calls //////////////////
+
+#define LOOPER_BEGIN(paint, type)                                   \
+/*    AutoValidator   validator(fMCRec->fTopLayer->fDevice); */     \
+    AutoDrawLooper  looper(this, paint);                            \
+    while (looper.next(type)) {                                     \
+        SkAutoBounderCommit ac(fBounder);                           \
+        SkDrawIter          iter(this);
+
+#define LOOPER_END    }
+
+////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::init(SkDevice* device) {
+    fBounder = NULL;
+    fLocalBoundsCompareType.setEmpty();
+    fLocalBoundsCompareTypeDirty = true;
+    fLocalBoundsCompareTypeBW.setEmpty();
+    fLocalBoundsCompareTypeDirtyBW = true;
+    fLastDeviceToGainFocus = NULL;
+    fDeviceCMDirty = false;
+    fLayerCount = 0;
+
+    fMCRec = (MCRec*)fMCStack.push_back();
+    new (fMCRec) MCRec(NULL, 0);
+
+    fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
+    fMCRec->fTopLayer = fMCRec->fLayer;
+    fMCRec->fNext = NULL;
+
+    fExternalMatrix.reset();
+    fExternalInverse.reset();
+    fUseExternalMatrix = false;
+
+    return this->setDevice(device);
+}
+
+SkCanvas::SkCanvas()
+: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    SkBitmap bitmap;
+    this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+}
+
+SkCanvas::SkCanvas(SkDevice* device)
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    this->init(device);
+}
+
+SkCanvas::SkCanvas(const SkBitmap& bitmap)
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+}
+
+SkCanvas::~SkCanvas() {
+    // free up the contents of our deque
+    this->restoreToCount(1);    // restore everything but the last
+    SkASSERT(0 == fLayerCount);
+
+    this->internalRestore();    // restore the last, since we're going away
+
+    SkSafeUnref(fBounder);
+
+    dec_canvas();
+}
+
+SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
+    SkRefCnt_SafeAssign(fBounder, bounder);
+    return bounder;
+}
+
+SkDrawFilter* SkCanvas::getDrawFilter() const {
+    return fMCRec->fFilter;
+}
+
+SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
+    SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
+    return filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::flush() {
+    SkDevice* device = this->getDevice();
+    if (device) {
+        device->flush();
+    }
+}
+
+SkISize SkCanvas::getDeviceSize() const {
+    SkDevice* d = this->getDevice();
+    return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
+}
+
+SkDevice* SkCanvas::getDevice() const {
+    // return root device
+    SkDeque::F2BIter iter(fMCStack);
+    MCRec*           rec = (MCRec*)iter.next();
+    SkASSERT(rec && rec->fLayer);
+    return rec->fLayer->fDevice;
+}
+
+SkDevice* SkCanvas::getTopDevice() const {
+    return fMCRec->fTopLayer->fDevice;
+}
+
+SkDevice* SkCanvas::setDevice(SkDevice* device) {
+    // return root device
+    SkDeque::F2BIter iter(fMCStack);
+    MCRec*           rec = (MCRec*)iter.next();
+    SkASSERT(rec && rec->fLayer);
+    SkDevice*       rootDevice = rec->fLayer->fDevice;
+
+    if (rootDevice == device) {
+        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();
+    }
+    if (rootDevice) {
+        rootDevice->unlockPixels();
+    }
+
+    SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
+    rootDevice = device;
+
+    fDeviceCMDirty = true;
+
+    /*  Now we update our initial region to have the bounds of the new device,
+        and then intersect all of the clips in our stack with these bounds,
+        to ensure that we can't draw outside of the device's bounds (and trash
+                                                                     memory).
+
+    NOTE: this is only a partial-fix, since if the new device is larger than
+        the previous one, we don't know how to "enlarge" the clips in our stack,
+        so drawing may be artificially restricted. Without keeping a history of
+        all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
+        reconstruct the correct clips, so this approximation will have to do.
+        The caller really needs to restore() back to the base if they want to
+        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;
+
+        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);
+        }
+    }
+    return device;
+}
+
+SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
+    SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
+    device->unref();
+    return device;
+}
+
+bool SkCanvas::readPixels(SkBitmap* bitmap,
+                          int x, int y,
+                          Config8888 config8888) {
+    SkDevice* device = this->getDevice();
+    if (!device) {
+        return false;
+    }
+    return device->readPixels(bitmap, x, y, config8888);
+}
+
+bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
+    SkDevice* device = this->getDevice();
+    if (!device) {
+        return false;
+    }
+
+    SkIRect bounds;
+    bounds.set(0, 0, device->width(), device->height());
+    if (!bounds.intersect(srcRect)) {
+        return false;
+    }
+
+    SkBitmap tmp;
+    tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
+                                               bounds.height());
+    if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
+        bitmap->swap(tmp);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
+                           Config8888 config8888) {
+    SkDevice* device = this->getDevice();
+    if (device) {
+        device->writePixels(bitmap, x, y, config8888);
+    }
+}
+
+SkCanvas* SkCanvas::canvasForDrawIter() {
+    return this;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::updateDeviceCMCache() {
+    if (fDeviceCMDirty) {
+        const SkMatrix& totalMatrix = this->getTotalMatrix();
+        const SkRasterClip& totalClip = *fMCRec->fRasterClip;
+        DeviceCM*       layer = fMCRec->fTopLayer;
+
+        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) {
+    int saveCount = this->getSaveCount(); // record this before the actual save
+
+    MCRec* newTop = (MCRec*)fMCStack.push_back();
+    new (newTop) MCRec(fMCRec, flags);    // balanced in restore()
+
+    newTop->fNext = fMCRec;
+    fMCRec = newTop;
+
+    fClipStack.save();
+    SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
+
+    return saveCount;
+}
+
+int SkCanvas::save(SaveFlags flags) {
+    // call shared impl
+    return this->internalSave(flags);
+}
+
+#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
+#define C16MASK (1 << SkBitmap::kRGB_565_Config)
+#define C8MASK  (1 << SkBitmap::kA8_Config)
+
+static SkBitmap::Config resolve_config(SkCanvas* canvas,
+                                       const SkIRect& bounds,
+                                       SkCanvas::SaveFlags flags,
+                                       bool* isOpaque) {
+    *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
+
+#if 0
+    // loop through and union all the configs we may draw into
+    uint32_t configMask = 0;
+    for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
+    {
+        SkDevice* device = canvas->getLayerDevice(i);
+        if (device->intersects(bounds))
+            configMask |= 1 << device->config();
+    }
+
+    // if the caller wants alpha or fullcolor, we can't return 565
+    if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
+                 SkCanvas::kHasAlphaLayer_SaveFlag))
+        configMask &= ~C16MASK;
+
+    switch (configMask) {
+    case C8MASK:    // if we only have A8, return that
+        return SkBitmap::kA8_Config;
+
+    case C16MASK:   // if we only have 565, return that
+        return SkBitmap::kRGB_565_Config;
+
+    default:
+        return SkBitmap::kARGB_8888_Config; // default answer
+    }
+#else
+    return SkBitmap::kARGB_8888_Config; // default answer
+#endif
+}
+
+static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
+    return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
+}
+
+bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
+                               SkIRect* intersection) {
+    SkIRect clipBounds;
+    if (!this->getClipDeviceBounds(&clipBounds)) {
+        return false;
+    }
+    SkIRect ir;
+    if (NULL != bounds) {
+        SkRect r;
+
+        this->getTotalMatrix().mapRect(&r, *bounds);
+        r.roundOut(&ir);
+        // early exit if the layer's bounds are clipped out
+        if (!ir.intersect(clipBounds)) {
+            if (bounds_affects_clip(flags)) {
+                fMCRec->fRasterClip->setEmpty();
+            }
+            return false;
+        }
+    } else {    // no user bounds, so just use the clip
+        ir = clipBounds;
+    }
+
+    fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
+
+    // early exit if the clip is now empty
+    if (bounds_affects_clip(flags) &&
+        !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
+        return false;
+    }
+
+    if (intersection) {
+        *intersection = ir;
+    }
+    return true;
+}
+
+int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                        SaveFlags flags) {
+    // 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);
+
+    fDeviceCMDirty = true;
+
+    SkIRect ir;
+    if (!this->clipRectBounds(bounds, flags, &ir)) {
+        return count;
+    }
+
+    // Kill the imagefilter if our device doesn't allow it
+    SkLazyPaint lazyP;
+    if (paint && paint->getImageFilter()) {
+        if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
+            SkPaint* p = lazyP.set(*paint);
+            p->setImageFilter(NULL);
+            paint = p;
+        }
+    }
+
+    bool isOpaque;
+    SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
+
+    SkDevice* device;
+    if (paint && paint->getImageFilter()) {
+        device = this->createCompatibleDevice(config, ir.width(), ir.height(),
+                                              isOpaque);
+    } else {
+        device = this->createLayerDevice(config, ir.width(), ir.height(),
+                                         isOpaque);
+    }
+    if (NULL == device) {
+        SkDebugf("Unable to create device for layer.");
+        return count;
+    }
+
+    device->setOrigin(ir.fLeft, ir.fTop);
+    DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
+    device->unref();
+
+    layer->fNext = fMCRec->fTopLayer;
+    fMCRec->fLayer = layer;
+    fMCRec->fTopLayer = layer;    // this field is NOT an owner of layer
+
+    fLayerCount += 1;
+    return count;
+}
+
+int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+                             SaveFlags flags) {
+    if (0xFF == alpha) {
+        return this->saveLayer(bounds, NULL, flags);
+    } else {
+        SkPaint tmpPaint;
+        tmpPaint.setAlpha(alpha);
+        return this->saveLayer(bounds, &tmpPaint, flags);
+    }
+}
+
+void SkCanvas::restore() {
+    // check for underflow
+    if (fMCStack.count() > 1) {
+        this->internalRestore();
+    }
+}
+
+void SkCanvas::internalRestore() {
+    SkASSERT(fMCStack.count() != 0);
+
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    fLocalBoundsCompareTypeDirtyBW = true;
+
+    fClipStack.restore();
+    // reserve our layer (if any)
+    DeviceCM* layer = fMCRec->fLayer;   // may be null
+    // now detach it from fMCRec so we can pop(). Gets freed after its drawn
+    fMCRec->fLayer = NULL;
+
+    // now do the normal restore()
+    fMCRec->~MCRec();       // balanced in save()
+    fMCStack.pop_back();
+    fMCRec = (MCRec*)fMCStack.back();
+
+    /*  Time to draw the layer's offscreen. We can't call the public drawSprite,
+        since if we're being recorded, we don't want to record this (the
+        recorder will have already recorded the restore).
+    */
+    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
+            fDeviceCMDirty = true;
+
+            SkASSERT(fLayerCount > 0);
+            fLayerCount -= 1;
+        }
+        SkDELETE(layer);
+    }
+
+    SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
+}
+
+int SkCanvas::getSaveCount() const {
+    return fMCStack.count();
+}
+
+void SkCanvas::restoreToCount(int count) {
+    // sanity check
+    if (count < 1) {
+        count = 1;
+    }
+
+    int n = this->getSaveCount() - count;
+    for (int i = 0; i < n; ++i) {
+        this->restore();
+    }
+}
+
+bool SkCanvas::isDrawingToLayer() const {
+    return fLayerCount > 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// can't draw it if its empty, or its too big for a fixed-point width or height
+static bool reject_bitmap(const SkBitmap& bitmap) {
+    return  bitmap.width() <= 0 || bitmap.height() <= 0
+#ifndef SK_ALLOW_OVER_32K_BITMAPS
+            || bitmap.width() > 32767 || bitmap.height() > 32767
+#endif
+            ;
+}
+
+void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
+                                const SkMatrix& matrix, const SkPaint* paint) {
+    if (reject_bitmap(bitmap)) {
+        return;
+    }
+
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
+    }
+    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) {
+    SkPaint tmp;
+    if (NULL == paint) {
+        tmp.setDither(true);
+        paint = &tmp;
+    }
+
+    LOOPER_BEGIN(*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);
+            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);
+            }
+        } else {
+            dstDev->drawDevice(iter, srcDev, 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;
+}
+
+// this is not virtual, so it must call a virtual method so that subclasses
+// will see its action
+void SkCanvas::resetMatrix() {
+    SkMatrix matrix;
+
+    matrix.reset();
+    this->setMatrix(matrix);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    AutoValidateClip avc(this);
+
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    fLocalBoundsCompareTypeDirtyBW = true;
+
+    if (fMCRec->fMatrix->rectStaysRect()) {
+        // for these simpler matrices, we can stay a rect ever after applying
+        // the matrix. This means we don't have to a) make a path, and b) tell
+        // the region code to scan-convert the path, only to discover that it
+        // is really just a rect.
+        SkRect      r;
+
+        fMCRec->fMatrix->mapRect(&r, rect);
+        fClipStack.clipDevRect(r, op, doAA);
+        return fMCRec->fRasterClip->op(r, op, doAA);
+    } else {
+        // since we're rotate or some such thing, we convert the rect to a path
+        // and clip against that, since it can handle any matrix. However, to
+        // avoid recursion in the case where we are subclassed (e.g. Pictures)
+        // we explicitly call "our" version of clipPath.
+        SkPath  path;
+
+        path.addRect(rect);
+        return this->SkCanvas::clipPath(path, op, doAA);
+    }
+}
+
+static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
+                           const SkPath& devPath, SkRegion::Op op, bool doAA) {
+    // base is used to limit the size (and therefore memory allocation) of the
+    // region that results from scan converting devPath.
+    SkRegion base;
+
+    if (SkRegion::kIntersect_Op == op) {
+        // since we are intersect, we can do better (tighter) with currRgn's
+        // bounds, than just using the device. However, if currRgn is complex,
+        // our region blitter may hork, so we do that case in two steps.
+        if (currClip->isRect()) {
+            return currClip->setPath(devPath, *currClip, doAA);
+        } else {
+            base.setRect(currClip->getBounds());
+            SkRasterClip clip;
+            clip.setPath(devPath, base, doAA);
+            return currClip->op(clip, op);
+        }
+    } else {
+        const SkDevice* device = canvas->getDevice();
+        if (!device) {
+            return currClip->setEmpty();
+        }
+
+        base.setRect(0, 0, device->width(), device->height());
+
+        if (SkRegion::kReplace_Op == op) {
+            return currClip->setPath(devPath, base, doAA);
+        } else {
+            SkRasterClip clip;
+            clip.setPath(devPath, base, doAA);
+            return currClip->op(clip, op);
+        }
+    }
+}
+
+bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    AutoValidateClip avc(this);
+
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    fLocalBoundsCompareTypeDirtyBW = true;
+
+    SkPath devPath;
+    path.transform(*fMCRec->fMatrix, &devPath);
+
+    // Check if the transfomation, or the original path itself
+    // made us empty. Note this can also happen if we contained NaN
+    // values. computing the bounds detects this, and will set our
+    // bounds to empty if that is the case. (see SkRect::set(pts, count))
+    if (devPath.getBounds().isEmpty()) {
+        // resetting the path will remove any NaN or other wanky values
+        // that might upset our scan converter.
+        devPath.reset();
+    }
+
+    // if we called path.swap() we could avoid a deep copy of this path
+    fClipStack.clipDevPath(devPath, op, doAA);
+
+    return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
+}
+
+bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
+    AutoValidateClip avc(this);
+
+    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());
+
+    return fMCRec->fRasterClip->op(rgn, op);
+}
+
+#ifdef SK_DEBUG
+void SkCanvas::validateClip() const {
+    // construct clipRgn from the clipstack
+    const SkDevice* device = this->getDevice();
+    if (!device) {
+        SkASSERT(this->getTotalClip().isEmpty());
+        return;
+    }
+
+    SkIRect ir;
+    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();
+        }
+    }
+
+#if 0   // enable this locally for testing
+    // now compare against the current rgn
+    const SkRegion& rgn = this->getTotalClip();
+    SkASSERT(rgn == tmpClip);
+#endif
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+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));
+    }
+}
+
+/*  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 {
+
+    if (!rect.isFinite())
+        return true;
+
+    if (fMCRec->fRasterClip->isEmpty()) {
+        return true;
+    }
+
+    if (fMCRec->fMatrix->hasPerspective()) {
+        SkRect dst;
+        fMCRec->fMatrix->mapRect(&dst, rect);
+        SkIRect idst;
+        dst.roundOut(&idst);
+        return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
+    } else {
+        const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
+
+        // for speed, do the most likely reject compares first
+        SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
+        SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
+        if (userT >= clipR.fBottom || userB <= clipR.fTop) {
+            return true;
+        }
+        SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
+        SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
+        if (userL >= clipR.fRight || userR <= clipR.fLeft) {
+            return true;
+        }
+        return false;
+    }
+}
+
+bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
+    return path.isEmpty() || this->quickReject(path.getBounds(), et);
+}
+
+static inline int pinIntForScalar(int x) {
+#ifdef SK_SCALAR_IS_FIXED
+    if (x < SK_MinS16) {
+        x = SK_MinS16;
+    } else if (x > SK_MaxS16) {
+        x = SK_MaxS16;
+    }
+#endif
+    return x;
+}
+
+bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
+    SkIRect ibounds;
+    if (!getClipDeviceBounds(&ibounds)) {
+        return false;
+    }
+
+    SkMatrix inverse;
+    // if we can't invert the CTM, we can't return local clip bounds
+    if (!fMCRec->fMatrix->invert(&inverse)) {
+        if (bounds) {
+            bounds->setEmpty();
+        }
+        return false;
+    }
+
+    if (NULL != bounds) {
+        SkRect r;
+        // adjust it outwards if we are antialiasing
+        int inset = (kAA_EdgeType == et);
+
+        // 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
+        // really returnt the correct answer, but its the best we can do given
+        // that we've promised to return SkRect (even though we support devices
+        // 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.fBottom + inset));
+        inverse.mapRect(bounds, r);
+    }
+    return true;
+}
+
+bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
+    const SkRasterClip& clip = *fMCRec->fRasterClip;
+    if (clip.isEmpty()) {
+        if (bounds) {
+            bounds->setEmpty();
+        }
+        return false;
+    }
+
+    if (NULL != bounds) {
+        *bounds = clip.getBounds();
+    }
+    return true;
+}
+
+const SkMatrix& SkCanvas::getTotalMatrix() const {
+    return *fMCRec->fMatrix;
+}
+
+SkCanvas::ClipType SkCanvas::getClipType() const {
+    if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
+    if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
+    return kComplex_ClipType;
+}
+
+const SkRegion& SkCanvas::getTotalClip() const {
+    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) {
+    SkDevice* device = this->getTopDevice();
+    if (device) {
+        return device->createCompatibleDeviceForSaveLayer(config, width, height,
+                                                          isOpaque);
+    } else {
+        return NULL;
+    }
+}
+
+SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
+                                           int width, int height,
+                                           bool isOpaque) {
+    SkDevice* device = this->getDevice();
+    if (device) {
+        return device->createCompatibleDevice(config, width, height, isOpaque);
+    } else {
+        return NULL;
+    }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//  These are the virtual drawing methods
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::clear(SkColor color) {
+    SkDrawIter  iter(this);
+
+    while (iter.next()) {
+        iter.fDevice->clear(color);
+    }
+}
+
+void SkCanvas::drawPaint(const SkPaint& paint) {
+    this->internalDrawPaint(paint);
+}
+
+void SkCanvas::internalDrawPaint(const SkPaint& paint) {
+    LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPaint(iter, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                          const SkPaint& paint) {
+    if ((long)count <= 0) {
+        return;
+    }
+
+    SkASSERT(pts != NULL);
+
+    LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(r, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+
+    LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawRect(iter, r, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
+        SkRect storage;
+        const SkRect& bounds = path.getBounds();
+        if (this->quickReject(paint.computeFastBounds(bounds, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+    if (path.isEmpty()) {
+        if (path.isInverseFillType()) {
+            this->internalDrawPaint(paint);
+        }
+        return;
+    }
+
+    LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPath(iter, path, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                          const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        SkRect bounds = {
+            x, y,
+            x + SkIntToScalar(bitmap.width()),
+            y + SkIntToScalar(bitmap.height())
+        };
+        if (paint) {
+            (void)paint->computeFastBounds(bounds, &bounds);
+        }
+        if (this->quickReject(bounds, paint2EdgeType(paint))) {
+            return;
+        }
+    }
+
+    SkMatrix matrix;
+    matrix.setTranslate(x, y);
+    this->internalDrawBitmap(bitmap, NULL, matrix, paint);
+}
+
+// this one is non-virtual, so it can be called safely by other canvas apis
+void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* 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
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        SkRect storage;
+        const SkRect* bounds = &dst;
+        if (paint) {
+            bounds = &paint->computeFastBounds(dst, &storage);
+        }
+        if (this->quickReject(*bounds, paint2EdgeType(paint))) {
+            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()));
+    }
+    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;
+    }
+    this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
+}
+
+void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                              const SkRect& dst, const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    this->internalDrawBitmapRect(bitmap, src, dst, paint);
+}
+
+void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+                                const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    this->internalDrawBitmap(bitmap, NULL, matrix, paint);
+}
+
+void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
+                                const SkMatrix& matrix, const SkPaint& paint) {
+    SkDEBUGCODE(bitmap.validate();)
+
+    LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
+                                      const SkIRect& center, const SkRect& dst,
+                                      const SkPaint* paint) {
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        SkRect storage;
+        const SkRect* bounds = &dst;
+        if (paint) {
+            bounds = &paint->computeFastBounds(dst, &storage);
+        }
+        if (this->quickReject(*bounds, paint2EdgeType(paint))) {
+            return;
+        }
+    }
+
+    const int32_t w = bitmap.width();
+    const int32_t h = bitmap.height();
+
+    SkIRect c = center;
+    // pin center to the bounds of the bitmap
+    c.fLeft = SkMax32(0, center.fLeft);
+    c.fTop = SkMax32(0, center.fTop);
+    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 };
+    SkScalar dstX[4] = {
+        dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
+        dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
+    };
+    SkScalar dstY[4] = {
+        dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
+        dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
+    };
+
+    if (dstX[1] > dstX[2]) {
+        dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
+        dstX[2] = dstX[1];
+    }
+
+    if (dstY[1] > dstY[2]) {
+        dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
+        dstY[2] = dstY[1];
+    }
+
+    SkIRect s;
+    SkRect  d;
+    for (int y = 0; y < 3; y++) {
+        s.fTop = srcY[y];
+        s.fBottom = srcY[y+1];
+        d.fTop = dstY[y];
+        d.fBottom = dstY[y+1];
+        for (int x = 0; x < 3; x++) {
+            s.fLeft = srcX[x];
+            s.fRight = srcX[x+1];
+            d.fLeft = dstX[x];
+            d.fRight = dstX[x+1];
+            this->internalDrawBitmapRect(bitmap, &s, d, paint);
+        }
+    }
+}
+
+void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                              const SkRect& dst, const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+
+    // Need a device entry-point, so gpu can use a mesh
+    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) {
+        SkDevice::TextFlags flags;
+        if (device->filterTextFlags(paint, &flags)) {
+            SkPaint* newPaint = fLazy.set(paint);
+            newPaint->setFlags(flags.fFlags);
+            newPaint->setHinting(flags.fHinting);
+            fPaint = newPaint;
+        } else {
+            fPaint = &paint;
+        }
+    }
+
+    const SkPaint& paint() const { return *fPaint; }
+
+private:
+    const SkPaint*  fPaint;
+    SkLazyPaint     fLazy;
+};
+
+void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
+                        const SkRect& r, SkScalar textSize) {
+    if (paint.getStyle() == SkPaint::kFill_Style) {
+        draw.fDevice->drawRect(draw, r, paint);
+    } else {
+        SkPaint p(paint);
+        p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
+        draw.fDevice->drawRect(draw, r, p);
+    }
+}
+
+void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
+                                   const char text[], size_t byteLength,
+                                   SkScalar x, SkScalar y) {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 ||
+        draw.fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    SkScalar    width = 0;
+    SkPoint     start;
+
+    start.set(0, 0);    // to avoid warning
+    if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
+                            SkPaint::kStrikeThruText_Flag)) {
+        width = paint.measureText(text, byteLength);
+
+        SkScalar offsetX = 0;
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            offsetX = SkScalarHalf(width);
+        } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
+            offsetX = width;
+        }
+        start.set(x - offsetX, y);
+    }
+
+    if (0 == width) {
+        return;
+    }
+
+    uint32_t flags = paint.getFlags();
+
+    if (flags & (SkPaint::kUnderlineText_Flag |
+                 SkPaint::kStrikeThruText_Flag)) {
+        SkScalar textSize = paint.getTextSize();
+        SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
+        SkRect   r;
+
+        r.fLeft = start.fX;
+        r.fRight = start.fX + width;
+
+        if (flags & SkPaint::kUnderlineText_Flag) {
+            SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
+                                             start.fY);
+            r.fTop = offset;
+            r.fBottom = offset + height;
+            DrawRect(draw, paint, r, textSize);
+        }
+        if (flags & SkPaint::kStrikeThruText_Flag) {
+            SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
+                                             start.fY);
+            r.fTop = offset;
+            r.fBottom = offset + height;
+            DrawRect(draw, paint, r, textSize);
+        }
+    }
+}
+
+void SkCanvas::drawText(const void* text, size_t byteLength,
+                        SkScalar x, SkScalar y, const SkPaint& paint) {
+    LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
+        iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
+        DrawTextDecorations(iter, dfp.paint(),
+                            static_cast<const char*>(text), byteLength, x, y);
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawPosText(const void* text, size_t byteLength,
+                           const SkPoint pos[], const SkPaint& paint) {
+    LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
+        iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
+                                  dfp.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
+                            const SkScalar xpos[], SkScalar constY,
+                            const SkPaint& paint) {
+    LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
+        iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
+                                  dfp.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                              const SkPath& path, const SkMatrix* matrix,
+                              const SkPaint& paint) {
+    LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
+                                     matrix, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
+                                 const SkPoint pos[], const SkPaint& paint,
+                                 const SkPath& path, const SkMatrix* matrix) {
+    LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
+                                        looper.paint(), path, matrix);
+    }
+
+    LOOPER_END
+}
+#endif
+
+void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                            const SkPoint verts[], const SkPoint texs[],
+                            const SkColor colors[], SkXfermode* xmode,
+                            const uint16_t indices[], int indexCount,
+                            const SkPaint& paint) {
+    LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
+                                   colors, xmode, indices, indexCount,
+                                   looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawData(const void* data, size_t length) {
+    // do nothing. Subclasses may do something with the data
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// These methods are NOT virtual, and therefore must call back into virtual
+// methods, rather than actually drawing themselves.
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+                        SkXfermode::Mode mode) {
+    SkPaint paint;
+
+    paint.setARGB(a, r, g, b);
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        paint.setXfermodeMode(mode);
+    }
+    this->drawPaint(paint);
+}
+
+void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
+    SkPaint paint;
+
+    paint.setColor(c);
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        paint.setXfermodeMode(mode);
+    }
+    this->drawPaint(paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
+    SkPoint pt;
+
+    pt.set(x, y);
+    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
+    SkPoint pt;
+    SkPaint paint;
+
+    pt.set(x, y);
+    paint.setColor(color);
+    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
+                        const SkPaint& paint) {
+    SkPoint pts[2];
+
+    pts[0].set(x0, y0);
+    pts[1].set(x1, y1);
+    this->drawPoints(kLines_PointMode, 2, pts, paint);
+}
+
+void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
+                              SkScalar right, SkScalar bottom,
+                              const SkPaint& paint) {
+    SkRect  r;
+
+    r.set(left, top, right, bottom);
+    this->drawRect(r, paint);
+}
+
+void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
+                          const SkPaint& paint) {
+    if (radius < 0) {
+        radius = 0;
+    }
+
+    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);
+}
+
+void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
+                             const SkPaint& paint) {
+    if (rx > 0 && ry > 0) {
+        if (paint.canComputeFastBounds()) {
+            SkRect storage;
+            if (this->quickReject(paint.computeFastBounds(r, &storage),
+                                  paint2EdgeType(&paint))) {
+                return;
+            }
+        }
+
+        SkPath  path;
+        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+        this->drawPath(path, 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) {
+    if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
+        this->drawOval(oval, paint);
+    } else {
+        SkPath  path;
+        if (useCenter) {
+            path.moveTo(oval.centerX(), oval.centerY());
+        }
+        path.arcTo(oval, startAngle, sweepAngle, !useCenter);
+        if (useCenter) {
+            path.close();
+        }
+        this->drawPath(path, paint);
+    }
+}
+
+void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
+                                const SkPath& path, SkScalar hOffset,
+                                SkScalar vOffset, const SkPaint& paint) {
+    SkMatrix    matrix;
+
+    matrix.setTranslate(hOffset, vOffset);
+    this->drawTextOnPath(text, byteLength, path, &matrix, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawPicture(SkPicture& picture) {
+    int saveCount = save();
+    picture.draw(this);
+    restoreToCount(saveCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
+    SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
+
+    SkASSERT(canvas);
+
+    fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
+    fDone = !fImpl->next();
+}
+
+SkCanvas::LayerIter::~LayerIter() {
+    fImpl->~SkDrawIter();
+}
+
+void SkCanvas::LayerIter::next() {
+    fDone = !fImpl->next();
+}
+
+SkDevice* SkCanvas::LayerIter::device() const {
+    return fImpl->getDevice();
+}
+
+const SkMatrix& SkCanvas::LayerIter::matrix() const {
+    return fImpl->getMatrix();
+}
+
+const SkPaint& SkCanvas::LayerIter::paint() const {
+    const SkPaint* paint = fImpl->getPaint();
+    if (NULL == paint) {
+        paint = &fDefaultPaint;
+    }
+    return *paint;
+}
+
+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(); }
diff --git a/legacy/src/core/SkChunkAlloc.cpp b/legacy/src/core/SkChunkAlloc.cpp
new file mode 100644
index 0000000..56b4abe
--- /dev/null
+++ b/legacy/src/core/SkChunkAlloc.cpp
@@ -0,0 +1,149 @@
+
+/*
+ * 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 "SkChunkAlloc.h"
+
+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;
+        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);
+        return ptr >= (const char*)(this + 1) && ptr < fFreePtr;
+    }
+};
+
+SkChunkAlloc::SkChunkAlloc(size_t minSize)
+    : fBlock(NULL), fMinSize(SkAlign4(minSize)), fPool(NULL), fTotalCapacity(0)
+{
+}
+
+SkChunkAlloc::~SkChunkAlloc() {
+    this->reset();
+}
+
+void SkChunkAlloc::reset() {
+    fBlock->freeChain();
+    fBlock = NULL;
+    fPool->freeChain();
+    fPool = NULL;
+    fTotalCapacity = 0;
+}
+
+void SkChunkAlloc::reuse() {
+    if (fPool && fBlock) {
+        fPool->tail()->fNext = fBlock;
+    }
+    fPool = fBlock;
+    fBlock = NULL;
+    fTotalCapacity = 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 < fMinSize)
+        size = fMinSize;
+
+    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;
+    }
+    return block;
+}
+
+void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) {
+    bytes = SkAlign4(bytes);
+
+    Block* block = fBlock;
+
+    if (block == NULL || bytes > block->fFreeSize) {
+        block = this->newBlock(bytes, ftype);
+        if (NULL == block) {
+            return NULL;
+        }
+        block->fNext = fBlock;
+        fBlock = block;
+    }
+
+    SkASSERT(block && bytes <= block->fFreeSize);
+    void* ptr = block->fFreePtr;
+
+    block->fFreeSize -= bytes;
+    block->fFreePtr += bytes;
+    return ptr;
+}
+
+size_t SkChunkAlloc::unalloc(void* ptr) {
+    size_t bytes = 0;
+    Block* block = fBlock;
+    if (block) {
+        char* cPtr = reinterpret_cast<char*>(ptr);
+        char* start = block->startOfData();
+        if (start <= cPtr && cPtr < block->fFreePtr) {
+            bytes = block->fFreePtr - cPtr;
+            block->fFreeSize += bytes;
+            block->fFreePtr = cPtr;
+        }
+    }
+    return bytes;
+}
+
+bool SkChunkAlloc::contains(const void* addr) const {
+    const Block* block = fBlock;
+    while (block) {
+        if (block->contains(addr)) {
+            return true;
+        }
+        block = block->fNext;
+    }
+    return false;
+}
+
diff --git a/src/core/SkClampRange.cpp b/legacy/src/core/SkClampRange.cpp
similarity index 100%
rename from src/core/SkClampRange.cpp
rename to legacy/src/core/SkClampRange.cpp
diff --git a/legacy/src/core/SkClipStack.cpp b/legacy/src/core/SkClipStack.cpp
new file mode 100644
index 0000000..f885240
--- /dev/null
+++ b/legacy/src/core/SkClipStack.cpp
@@ -0,0 +1,241 @@
+
+/*
+ * 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 "SkClipStack.h"
+#include "SkPath.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;
+
+    Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
+        fSaveCount = saveCount;
+        fOp = op;
+        fState = kRect_State;
+        fDoAA = doAA;
+    }
+
+    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)) {
+    *this = b;
+}
+
+SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
+    if (this == &b) {
+        return *this;
+    }
+    reset();
+
+    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);
+    }
+
+    return *this;
+}
+
+bool SkClipStack::operator==(const SkClipStack& b) const {
+    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();
+
+    while (myRec != NULL && bRec != NULL) {
+        if (*myRec != *bRec) {
+            return false;
+        }
+        myRec = (const Rec*)myIter.next();
+        bRec = (const Rec*)bIter.next();
+    }
+    return myRec == NULL && bRec == NULL;
+}
+
+void SkClipStack::reset() {
+    // don't have a reset() on SkDeque, so fake it here
+    fDeque.~SkDeque();
+    new (&fDeque) SkDeque(sizeof(Rec));
+
+    fSaveCount = 0;
+}
+
+void SkClipStack::save() {
+    fSaveCount += 1;
+}
+
+void SkClipStack::restore() {
+    fSaveCount -= 1;
+    while (!fDeque.empty()) {
+        Rec* rec = (Rec*)fDeque.back();
+        if (rec->fSaveCount <= fSaveCount) {
+            break;
+        }
+        rec->~Rec();
+        fDeque.pop_back();
+    }
+}
+
+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:
+                return;
+            case Rec::kRect_State:
+                if (!rec->fRect.intersect(rect)) {
+                    rec->fState = Rec::kEmpty_State;
+                }
+                return;
+            case Rec::kPath_State:
+                if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
+                    rec->fState = Rec::kEmpty_State;
+                    return;
+                }
+                break;
+        }
+    }
+    new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
+}
+
+void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    Rec* rec = (Rec*)fDeque.back();
+    if (rec && rec->canBeIntersected(fSaveCount, op)) {
+        const SkRect& pathBounds = path.getBounds();
+        switch (rec->fState) {
+            case Rec::kEmpty_State:
+                return;
+            case Rec::kRect_State:
+                if (!SkRect::Intersects(rec->fRect, pathBounds)) {
+                    rec->fState = Rec::kEmpty_State;
+                    return;
+                }
+                break;
+            case Rec::kPath_State:
+                if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
+                    rec->fState = Rec::kEmpty_State;
+                    return;
+                }
+                break;
+        }
+    }
+    new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkClipStack::B2FIter::B2FIter() {
+}
+
+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));
+}
+
+bool operator!=(const SkClipStack::B2FIter::Clip& a,
+               const SkClipStack::B2FIter::Clip& b) {
+    return !(a == b);
+}
+
+SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) {
+    this->reset(stack);
+}
+
+const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() {
+    const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
+    if (NULL == rec) {
+        return NULL;
+    }
+
+    switch (rec->fState) {
+        case SkClipStack::Rec::kEmpty_State:
+            fClip.fRect = NULL;
+            fClip.fPath = NULL;
+            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;
+}
+
+void SkClipStack::B2FIter::reset(const SkClipStack& stack) {
+    fIter.reset(stack.fDeque);
+}
diff --git a/legacy/src/core/SkColor.cpp b/legacy/src/core/SkColor.cpp
new file mode 100644
index 0000000..fe7b8f8
--- /dev/null
+++ b/legacy/src/core/SkColor.cpp
@@ -0,0 +1,114 @@
+
+/*
+ * 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 "SkColor.h"
+#include "SkColorPriv.h"
+
+SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    return SkPremultiplyARGBInline(a, r, g, b);
+}
+
+SkPMColor SkPreMultiplyColor(SkColor c) {
+    return SkPremultiplyARGBInline(SkColorGetA(c), SkColorGetR(c),
+                                   SkColorGetG(c), SkColorGetB(c));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline SkScalar ByteToScalar(U8CPU x) {
+    SkASSERT(x <= 255);
+    return SkIntToScalar(x) / 255;
+}
+
+static inline SkScalar ByteDivToScalar(int numer, U8CPU denom) {
+    // cast to keep the answer signed
+    return SkIntToScalar(numer) / (int)denom;
+}
+
+void SkRGBToHSV(U8CPU r, U8CPU g, U8CPU b, SkScalar hsv[3]) {
+    SkASSERT(hsv);
+
+    unsigned min = SkMin32(r, SkMin32(g, b));
+    unsigned max = SkMax32(r, SkMax32(g, b));
+    unsigned delta = max - min;
+
+    SkScalar v = ByteToScalar(max);
+    SkASSERT(v >= 0 && v <= SK_Scalar1);
+
+    if (0 == delta) { // we're a shade of gray
+        hsv[0] = 0;
+        hsv[1] = 0;
+        hsv[2] = v;
+        return;
+    }
+
+    SkScalar s = ByteDivToScalar(delta, max);
+    SkASSERT(s >= 0 && s <= SK_Scalar1);
+
+    SkScalar h;    
+    if (r == max) {
+        h = ByteDivToScalar(g - b, delta);
+    } else if (g == max) {
+        h = SkIntToScalar(2) + ByteDivToScalar(b - r, delta);
+    } else { // b == max
+        h = SkIntToScalar(4) + ByteDivToScalar(r - g, delta);
+    }
+
+    h *= 60;
+    if (h < 0) {
+        h += SkIntToScalar(360);
+    }
+    SkASSERT(h >= 0 && h < SkIntToScalar(360));
+
+    hsv[0] = h;
+    hsv[1] = s;
+    hsv[2] = v;
+}
+
+static inline U8CPU UnitScalarToByte(SkScalar x) {
+    if (x < 0) {
+        return 0;
+    }
+    if (x >= SK_Scalar1) {
+        return 255;
+    }
+    return SkScalarToFixed(x) >> 8;
+}
+
+SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) {
+    SkASSERT(hsv);
+
+    U8CPU s = UnitScalarToByte(hsv[1]);
+    U8CPU v = UnitScalarToByte(hsv[2]);
+
+    if (0 == s) { // shade of gray
+        return SkColorSetARGB(a, v, v, v);
+    }
+    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);
+    switch (hx >> 16) {
+        case 0: r = v; g = t; b = p; break;
+        case 1: r = q; g = v; b = p; break;
+        case 2: r = p; g = v; b = t; break;
+        case 3: r = p; g = q; b = v; break;
+        case 4: r = t;  g = p; b = v; break;
+        default: r = v; g = p; b = q; break;
+    }
+    return SkColorSetARGB(a, r, g, b);
+}
+
diff --git a/legacy/src/core/SkColorFilter.cpp b/legacy/src/core/SkColorFilter.cpp
new file mode 100644
index 0000000..2ca88bb
--- /dev/null
+++ b/legacy/src/core/SkColorFilter.cpp
@@ -0,0 +1,109 @@
+
+/*
+ * 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 "SkColorFilter.h"
+#include "SkShader.h"
+#include "SkUnPreMultiply.h"
+
+bool SkColorFilter::asColorMode(SkColor* color, SkXfermode::Mode* mode) {
+    return false;
+}
+
+bool SkColorFilter::asColorMatrix(SkScalar matrix[20]) {
+    return false;
+}
+
+bool SkColorFilter::asComponentTable(SkBitmap*) {
+    return false;
+}
+
+void SkColorFilter::filterSpan16(const uint16_t s[], int count, uint16_t d[]) {
+    SkASSERT(this->getFlags() & SkColorFilter::kHasFilter16_Flag);
+    SkDEBUGFAIL("missing implementation of SkColorFilter::filterSpan16");
+
+    if (d != s) {
+        memcpy(d, s, count * sizeof(uint16_t));
+    }
+}
+
+SkColor SkColorFilter::filterColor(SkColor c) {
+    SkPMColor dst, src = SkPreMultiplyColor(c);
+    this->filterSpan(&src, 1, &dst);
+    return SkUnPreMultiply::PMColorToColor(dst);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter) {
+    fShader = shader;   shader->ref();
+    fFilter = filter;   filter->ref();
+}
+
+SkFilterShader::SkFilterShader(SkFlattenableReadBuffer& buffer) :
+        INHERITED(buffer) {
+    fShader = static_cast<SkShader*>(buffer.readFlattenable());
+    fFilter = static_cast<SkColorFilter*>(buffer.readFlattenable());
+}
+
+SkFilterShader::~SkFilterShader() {
+    fFilter->unref();
+    fShader->unref();
+}
+
+void SkFilterShader::beginSession() {
+    this->INHERITED::beginSession();
+    fShader->beginSession();
+}
+
+void SkFilterShader::endSession() {
+    fShader->endSession();
+    this->INHERITED::endSession();
+}
+
+void SkFilterShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fShader);
+    buffer.writeFlattenable(fFilter);
+}
+
+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;
+    }
+    // if the filter might change alpha, clear the opaque flag in the shader
+    if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) {
+        shaderF &= ~(SkShader::kOpaqueAlpha_Flag | SkShader::kHasSpan16_Flag);
+    }
+    return shaderF;
+}
+
+bool SkFilterShader::setContext(const SkBitmap& device,
+                                const SkPaint& paint,
+                                const SkMatrix& matrix) {
+    return  this->INHERITED::setContext(device, paint, matrix) &&
+            fShader->setContext(device, paint, matrix);
+}
+
+void SkFilterShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
+    fShader->shadeSpan(x, y, result, count);
+    fFilter->filterSpan(result, count, result);
+}
+
+void SkFilterShader::shadeSpan16(int x, int y, uint16_t result[], int count) {
+    SkASSERT(fShader->getFlags() & SkShader::kHasSpan16_Flag);
+    SkASSERT(fFilter->getFlags() & SkColorFilter::kHasFilter16_Flag);
+
+    fShader->shadeSpan16(x, y, result, count);
+    fFilter->filterSpan16(result, count, result);
+}
+
diff --git a/legacy/src/core/SkColorTable.cpp b/legacy/src/core/SkColorTable.cpp
new file mode 100644
index 0000000..4a9480d
--- /dev/null
+++ b/legacy/src/core/SkColorTable.cpp
@@ -0,0 +1,158 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+SkColorTable::SkColorTable(int count)
+    : f16BitCache(NULL), fFlags(0)
+{
+    if (count < 0)
+        count = 0;
+    else if (count > 256)
+        count = 256;
+
+    fCount = SkToU16(count);
+    fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
+    memset(fColors, 0, count * sizeof(SkPMColor));
+
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+}
+
+// call SkRefCnt's constructor explicitly, to avoid warning
+SkColorTable::SkColorTable(const SkColorTable& src) : SkRefCnt() {
+    f16BitCache = NULL;
+    fFlags = src.fFlags;
+    int count = src.count();
+    fCount = SkToU16(count);
+    fColors = reinterpret_cast<SkPMColor*>(
+                                    sk_malloc_throw(count * sizeof(SkPMColor)));
+    memcpy(fColors, src.fColors, count * sizeof(SkPMColor));
+
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+}
+
+SkColorTable::SkColorTable(const SkPMColor colors[], int count)
+    : f16BitCache(NULL), fFlags(0)
+{
+    if (count < 0)
+        count = 0;
+    else if (count > 256)
+        count = 256;
+
+    fCount = SkToU16(count);
+    fColors = reinterpret_cast<SkPMColor*>(
+                                    sk_malloc_throw(count * sizeof(SkPMColor)));
+
+    if (colors)
+        memcpy(fColors, colors, count * sizeof(SkPMColor));
+
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+}
+
+SkColorTable::~SkColorTable()
+{
+    SkASSERT(fColorLockCount == 0);
+    SkASSERT(f16BitCacheLockCount == 0);
+
+    sk_free(fColors);
+    sk_free(f16BitCache);
+}
+
+void SkColorTable::setFlags(unsigned flags)
+{
+    fFlags = SkToU8(flags);
+}
+
+void SkColorTable::unlockColors(bool changed)
+{
+    SkASSERT(fColorLockCount != 0);
+    SkDEBUGCODE(fColorLockCount -= 1;)
+    if (changed)
+        this->inval16BitCache();
+}
+
+void SkColorTable::inval16BitCache()
+{
+    SkASSERT(f16BitCacheLockCount == 0);
+    if (f16BitCache)
+    {
+        sk_free(f16BitCache);
+        f16BitCache = NULL;
+    }
+}
+
+#include "SkColorPriv.h"
+
+static inline void build_16bitcache(uint16_t dst[], const SkPMColor src[], int count)
+{
+    while (--count >= 0)
+        *dst++ = SkPixel32ToPixel16_ToU16(*src++);
+}
+
+const uint16_t* SkColorTable::lock16BitCache()
+{
+    if (fFlags & kColorsAreOpaque_Flag)
+    {
+        if (f16BitCache == NULL) // build the cache
+        {
+            f16BitCache = (uint16_t*)sk_malloc_throw(fCount * sizeof(uint16_t));
+            build_16bitcache(f16BitCache, fColors, fCount);
+        }
+    }
+    else    // our colors have alpha, so no cache
+    {
+        this->inval16BitCache();
+        if (f16BitCache)
+        {
+            sk_free(f16BitCache);
+            f16BitCache = NULL;
+        }
+    }
+
+    SkDEBUGCODE(f16BitCacheLockCount += 1);
+    return f16BitCache;
+}
+
+void SkColorTable::setIsOpaque(bool isOpaque) {
+    if (isOpaque) {
+        fFlags |= kColorsAreOpaque_Flag;
+    } else {
+        fFlags &= ~kColorsAreOpaque_Flag;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkColorTable::SkColorTable(SkFlattenableReadBuffer& buffer) {
+    f16BitCache = NULL;
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+
+    fCount = buffer.readU16();
+    SkASSERT((unsigned)fCount <= 256);
+
+    fFlags = buffer.readU8();
+
+    fColors = (SkPMColor*)sk_malloc_throw(fCount * sizeof(SkPMColor));
+    buffer.read(fColors, fCount * sizeof(SkPMColor));
+}
+
+void SkColorTable::flatten(SkFlattenableWriteBuffer& buffer) const {
+    int count = this->count();
+    buffer.write16(count);
+    buffer.write8(this->getFlags());
+    buffer.writeMul4(fColors, count * sizeof(SkPMColor));
+}
+
diff --git a/legacy/src/core/SkComposeShader.cpp b/legacy/src/core/SkComposeShader.cpp
new file mode 100644
index 0000000..c8d3299
--- /dev/null
+++ b/legacy/src/core/SkComposeShader.cpp
@@ -0,0 +1,167 @@
+
+/*
+ * 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 "SkComposeShader.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkColorShader.h"
+#include "SkXfermode.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
+    fShaderA = sA;  sA->ref();
+    fShaderB = sB;  sB->ref();
+    // mode may be null
+    fMode = mode;
+    SkSafeRef(mode);
+}
+
+SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
+    INHERITED(buffer) {
+    fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
+    if (NULL == fShaderA) {
+        fShaderA = SkNEW_ARGS(SkColorShader, (0));
+    }
+    fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
+    if (NULL == fShaderB) {
+        fShaderB = SkNEW_ARGS(SkColorShader, (0));
+    }
+    fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
+}
+
+SkComposeShader::~SkComposeShader() {
+    SkSafeUnref(fMode);
+    fShaderB->unref();
+    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) {
+        fAlpha = paint->getAlpha();
+        fPaint = paint;
+        paint->setAlpha(newAlpha);
+    }
+
+    ~SkAutoAlphaRestore() {
+        fPaint->setAlpha(fAlpha);
+    }
+private:
+    SkPaint*    fPaint;
+    uint8_t     fAlpha;
+};
+
+void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fShaderA);
+    buffer.writeFlattenable(fShaderB);
+    buffer.writeFlattenable(fMode);
+}
+
+/*  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.
+*/
+bool SkComposeShader::setContext(const SkBitmap& device,
+                                 const SkPaint& paint,
+                                 const SkMatrix& matrix) {
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    // we preconcat our localMatrix (if any) with the device matrix
+    // before calling our sub-shaders
+
+    SkMatrix tmpM;
+
+    (void)this->getLocalMatrix(&tmpM);
+    tmpM.setConcat(matrix, tmpM);
+
+    SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
+
+    return  fShaderA->setContext(device, paint, tmpM) &&
+            fShaderB->setContext(device, paint, tmpM);
+}
+
+// larger is better (fewer times we have to loop), but we shouldn't
+// take up too much stack-space (each element is 4 bytes)
+#define TMP_COLOR_COUNT     64
+
+void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
+    SkShader*   shaderA = fShaderA;
+    SkShader*   shaderB = fShaderB;
+    SkXfermode* mode = fMode;
+    unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
+
+    SkPMColor   tmp[TMP_COLOR_COUNT];
+
+    if (NULL == mode) {   // implied SRC_OVER
+        // TODO: when we have a good test-case, should use SkBlitRow::Proc32
+        // for these loops
+        do {
+            int n = count;
+            if (n > TMP_COLOR_COUNT) {
+                n = TMP_COLOR_COUNT;
+            }
+
+            shaderA->shadeSpan(x, y, result, n);
+            shaderB->shadeSpan(x, y, tmp, n);
+
+            if (256 == scale) {
+                for (int i = 0; i < n; i++) {
+                    result[i] = SkPMSrcOver(tmp[i], result[i]);
+                }
+            } else {
+                for (int i = 0; i < n; i++) {
+                    result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
+                                            scale);
+                }
+            }
+
+            result += n;
+            x += n;
+            count -= n;
+        } while (count > 0);
+    } else {    // use mode for the composition
+        do {
+            int n = count;
+            if (n > TMP_COLOR_COUNT) {
+                n = TMP_COLOR_COUNT;
+            }
+
+            shaderA->shadeSpan(x, y, result, n);
+            shaderB->shadeSpan(x, y, tmp, n);
+            mode->xfer32(result, tmp, n, NULL);
+
+            if (256 == scale) {
+                for (int i = 0; i < n; i++) {
+                    result[i] = SkAlphaMulQ(result[i], scale);
+                }
+            }
+
+            result += n;
+            x += n;
+            count -= n;
+        } while (count > 0);
+    }
+}
+
diff --git a/src/core/SkConcaveToTriangles.cpp b/legacy/src/core/SkConcaveToTriangles.cpp
similarity index 100%
rename from src/core/SkConcaveToTriangles.cpp
rename to legacy/src/core/SkConcaveToTriangles.cpp
diff --git a/src/core/SkConcaveToTriangles.h b/legacy/src/core/SkConcaveToTriangles.h
similarity index 100%
rename from src/core/SkConcaveToTriangles.h
rename to legacy/src/core/SkConcaveToTriangles.h
diff --git a/legacy/src/core/SkConfig8888.cpp b/legacy/src/core/SkConfig8888.cpp
new file mode 100644
index 0000000..10a1b36
--- /dev/null
+++ b/legacy/src/core/SkConfig8888.cpp
@@ -0,0 +1,281 @@
+#include "SkConfig8888.h"
+
+namespace {
+
+template <int A_IDX, int R_IDX, int G_IDX, int B_IDX>
+inline uint32_t pack_config8888(uint32_t a, uint32_t r,
+                                uint32_t g, uint32_t b) {
+#ifdef SK_CPU_LENDIAN
+    return (a << (A_IDX * 8)) | (r << (R_IDX * 8)) |
+           (g << (G_IDX * 8)) | (b << (B_IDX * 8));
+#else
+    return (a << ((3-A_IDX) * 8)) | (r << ((3-R_IDX) * 8)) |
+           (g << ((3-G_IDX) * 8)) | (b << ((3-B_IDX) * 8));
+#endif
+}
+
+template <int A_IDX, int R_IDX, int G_IDX, int B_IDX>
+inline void unpack_config8888(uint32_t color,
+                              uint32_t* a, uint32_t* r,
+                              uint32_t* g, uint32_t* b) {
+#ifdef SK_CPU_LENDIAN
+    *a = (color >> (A_IDX * 8)) & 0xff;
+    *r = (color >> (R_IDX * 8)) & 0xff;
+    *g = (color >> (G_IDX * 8)) & 0xff;
+    *b = (color >> (B_IDX * 8)) & 0xff;
+#else
+    *a = (color >> ((3 - A_IDX) * 8)) & 0xff;
+    *r = (color >> ((3 - R_IDX) * 8)) & 0xff;
+    *g = (color >> ((3 - G_IDX) * 8)) & 0xff;
+    *b = (color >> ((3 - B_IDX) * 8)) & 0xff;
+#endif
+}
+
+#ifdef SK_CPU_LENDIAN
+    static const int SK_NATIVE_A_IDX = SK_A32_SHIFT / 8;
+    static const int SK_NATIVE_R_IDX = SK_R32_SHIFT / 8;
+    static const int SK_NATIVE_G_IDX = SK_G32_SHIFT / 8;
+    static const int SK_NATIVE_B_IDX = SK_B32_SHIFT / 8;
+#else
+    static const int SK_NATIVE_A_IDX = 3 - (SK_A32_SHIFT / 8);
+    static const int SK_NATIVE_R_IDX = 3 - (SK_R32_SHIFT / 8);
+    static const int SK_NATIVE_G_IDX = 3 - (SK_G32_SHIFT / 8);
+    static const int SK_NATIVE_B_IDX = 3 - (SK_B32_SHIFT / 8);
+#endif
+
+/**
+ * convert_pixel<OUT_CFG, IN_CFG converts a pixel value from one Config8888 to
+ * another. It is implemented by first expanding OUT_CFG to r, g, b, a indices
+ * and an is_premul bool as params to another template function. Then IN_CFG is
+ * expanded via another function call.
+ */
+
+template <bool OUT_PM, int OUT_A_IDX, int OUT_R_IDX, int OUT_G_IDX, int OUT_B_IDX,
+          bool IN_PM,  int IN_A_IDX,  int IN_R_IDX,  int IN_G_IDX,  int IN_B_IDX>
+inline uint32_t convert_pixel(uint32_t pixel) {
+    uint32_t a, r, g, b;
+    unpack_config8888<IN_A_IDX, IN_R_IDX, IN_G_IDX, IN_B_IDX>(pixel, &a, &r, &g, &b);
+    if (IN_PM && !OUT_PM) {
+        // We're doing the explicit divide to match WebKit layout
+        // test expectations. We can modify and rebaseline if there
+        // it can be shown that there is a more performant way to
+        // unpremul.
+        if (a) {
+            r = r * 0xff / a;
+            g = g * 0xff / a;
+            b = b * 0xff / a;
+        } else {
+            return 0;
+        }
+    } else if (!IN_PM && OUT_PM) {
+        // This matches WebKit's conversion which we are replacing.
+        // We can consider alternative rounding rules for performance.
+        r = SkMulDiv255Ceiling(r, a);
+        g = SkMulDiv255Ceiling(g, a);
+        b = SkMulDiv255Ceiling(b, a);
+    }
+    return pack_config8888<OUT_A_IDX, OUT_R_IDX, OUT_G_IDX, OUT_B_IDX>(a, r, g, b);
+}
+
+template <bool OUT_PM, int OUT_A_IDX, int OUT_R_IDX, int OUT_G_IDX, int OUT_B_IDX, SkCanvas::Config8888 IN_CFG>
+inline uint32_t convert_pixel(uint32_t pixel) {
+    switch(IN_CFG) {
+        case SkCanvas::kNative_Premul_Config8888:
+            return convert_pixel<OUT_PM, OUT_A_IDX,       OUT_R_IDX,       OUT_G_IDX,       OUT_B_IDX,
+                                 true,  SK_NATIVE_A_IDX,  SK_NATIVE_R_IDX, SK_NATIVE_G_IDX, SK_NATIVE_B_IDX>(pixel);
+            break;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            return convert_pixel<OUT_PM, OUT_A_IDX,       OUT_R_IDX,       OUT_G_IDX,       OUT_B_IDX,
+                                 false,  SK_NATIVE_A_IDX, SK_NATIVE_R_IDX, SK_NATIVE_G_IDX, SK_NATIVE_B_IDX>(pixel);
+            break;
+        case SkCanvas::kBGRA_Premul_Config8888:
+            return convert_pixel<OUT_PM, OUT_A_IDX, OUT_R_IDX, OUT_G_IDX, OUT_B_IDX,
+                                 true,  3,         2,         1,         0>(pixel);
+            break;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            return convert_pixel<OUT_PM, OUT_A_IDX, OUT_R_IDX, OUT_G_IDX, OUT_B_IDX,
+                                 false,  3,         2,         1,         0>(pixel);
+            break;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            return convert_pixel<OUT_PM, OUT_A_IDX, OUT_R_IDX, OUT_G_IDX, OUT_B_IDX,
+                                 true,  3,         0,         1,         2>(pixel);
+            break;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            return convert_pixel<OUT_PM, OUT_A_IDX, OUT_R_IDX, OUT_G_IDX, OUT_B_IDX,
+                                 false,  3,         0,         1,         2>(pixel);
+            break;
+        default:
+            SkDEBUGFAIL("Unexpected config8888");
+            return 0;
+            break;
+    }
+}
+
+template <SkCanvas::Config8888 OUT_CFG, SkCanvas::Config8888 IN_CFG>
+inline uint32_t convert_pixel(uint32_t pixel) {
+    switch(OUT_CFG) {
+        case SkCanvas::kNative_Premul_Config8888:
+            return convert_pixel<true,  SK_NATIVE_A_IDX,  SK_NATIVE_R_IDX, SK_NATIVE_G_IDX, SK_NATIVE_B_IDX, IN_CFG>(pixel);
+            break;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            return convert_pixel<false,  SK_NATIVE_A_IDX,  SK_NATIVE_R_IDX, SK_NATIVE_G_IDX, SK_NATIVE_B_IDX, IN_CFG>(pixel);
+            break;
+        case SkCanvas::kBGRA_Premul_Config8888:
+            return convert_pixel<true, 3, 2, 1, 0, IN_CFG>(pixel);
+            break;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            return convert_pixel<false, 3, 2, 1, 0, IN_CFG>(pixel);
+            break;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            return convert_pixel<true, 3, 0, 1, 2, IN_CFG>(pixel);
+            break;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            return convert_pixel<false, 3, 0, 1, 2, IN_CFG>(pixel);
+            break;
+        default:
+            SkDEBUGFAIL("Unexpected config8888");
+            return 0;
+            break;
+    }
+}
+
+/**
+ * SkConvertConfig8888Pixels has 6 * 6 possible combinations of src and dst
+ * configs. Each is implemented as an instantiation templated function. Two
+ * levels of switch statements are used to select the correct instantiation, one
+ * for the src config and one for the dst config.
+ */
+
+template <SkCanvas::Config8888 DST_CFG, SkCanvas::Config8888 SRC_CFG>
+inline void convert_config8888(uint32_t* dstPixels,
+                               size_t dstRowBytes,
+                               const uint32_t* srcPixels,
+                               size_t srcRowBytes,
+                               int width,
+                               int height) {
+    intptr_t dstPix = reinterpret_cast<intptr_t>(dstPixels);
+    intptr_t srcPix = reinterpret_cast<intptr_t>(srcPixels);
+
+    for (int y = 0; y < height; ++y) {
+        srcPixels = reinterpret_cast<const uint32_t*>(srcPix);
+        dstPixels = reinterpret_cast<uint32_t*>(dstPix);
+        for (int x = 0; x < width; ++x) {
+            dstPixels[x] = convert_pixel<DST_CFG, SRC_CFG>(srcPixels[x]);
+        }
+        dstPix += dstRowBytes;
+        srcPix += srcRowBytes;
+    }
+}
+
+template <SkCanvas::Config8888 SRC_CFG>
+inline void convert_config8888(uint32_t* dstPixels,
+                               size_t dstRowBytes,
+                               SkCanvas::Config8888 dstConfig,
+                               const uint32_t* srcPixels,
+                               size_t srcRowBytes,
+                               int width,
+                               int height) {
+    switch(dstConfig) {
+        case SkCanvas::kNative_Premul_Config8888:
+            convert_config8888<SkCanvas::kNative_Premul_Config8888, SRC_CFG>(dstPixels, dstRowBytes, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            convert_config8888<SkCanvas::kNative_Unpremul_Config8888, SRC_CFG>(dstPixels, dstRowBytes, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kBGRA_Premul_Config8888:
+            convert_config8888<SkCanvas::kBGRA_Premul_Config8888, SRC_CFG>(dstPixels, dstRowBytes, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            convert_config8888<SkCanvas::kBGRA_Unpremul_Config8888, SRC_CFG>(dstPixels, dstRowBytes, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            convert_config8888<SkCanvas::kRGBA_Premul_Config8888, SRC_CFG>(dstPixels, dstRowBytes, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            convert_config8888<SkCanvas::kRGBA_Unpremul_Config8888, SRC_CFG>(dstPixels, dstRowBytes, srcPixels, srcRowBytes, width, height);
+            break;
+        default:
+            SkDEBUGFAIL("Unexpected config8888");
+            break;
+    }
+}
+
+}
+
+void SkConvertConfig8888Pixels(uint32_t* dstPixels,
+                               size_t dstRowBytes,
+                               SkCanvas::Config8888 dstConfig,
+                               const uint32_t* srcPixels,
+                               size_t srcRowBytes,
+                               SkCanvas::Config8888 srcConfig,
+                               int width,
+                               int height) {
+    if (srcConfig == dstConfig) {
+        if (srcPixels == dstPixels) {
+            return;
+        }
+        if (dstRowBytes == srcRowBytes &&
+            4U * width == srcRowBytes) {
+            memcpy(dstPixels, srcPixels, srcRowBytes * height);
+            return;
+        } else {
+            intptr_t srcPix = reinterpret_cast<intptr_t>(srcPixels);
+            intptr_t dstPix = reinterpret_cast<intptr_t>(dstPixels);
+            for (int y = 0; y < height; ++y) {
+                srcPixels = reinterpret_cast<const uint32_t*>(srcPix);
+                dstPixels = reinterpret_cast<uint32_t*>(dstPix);
+                memcpy(dstPixels, srcPixels, 4 * width);
+                srcPix += srcRowBytes;
+                dstPix += dstRowBytes;
+            }
+            return;
+        }
+    }
+    switch(srcConfig) {
+        case SkCanvas::kNative_Premul_Config8888:
+            convert_config8888<SkCanvas::kNative_Premul_Config8888>(dstPixels, dstRowBytes, dstConfig, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            convert_config8888<SkCanvas::kNative_Unpremul_Config8888>(dstPixels, dstRowBytes, dstConfig, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kBGRA_Premul_Config8888:
+            convert_config8888<SkCanvas::kBGRA_Premul_Config8888>(dstPixels, dstRowBytes, dstConfig, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            convert_config8888<SkCanvas::kBGRA_Unpremul_Config8888>(dstPixels, dstRowBytes, dstConfig, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            convert_config8888<SkCanvas::kRGBA_Premul_Config8888>(dstPixels, dstRowBytes, dstConfig, srcPixels, srcRowBytes, width, height);
+            break;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            convert_config8888<SkCanvas::kRGBA_Unpremul_Config8888>(dstPixels, dstRowBytes, dstConfig, srcPixels, srcRowBytes, width, height);
+            break;
+        default:
+            SkDEBUGFAIL("Unexpected config8888");
+            break;
+    }
+}
+
+uint32_t SkPackConfig8888(SkCanvas::Config8888 config,
+                          uint32_t a,
+                          uint32_t r,
+                          uint32_t g,
+                          uint32_t b) {
+    switch (config) {
+        case SkCanvas::kNative_Premul_Config8888:
+        case SkCanvas::kNative_Unpremul_Config8888:
+            return pack_config8888<SK_NATIVE_A_IDX,
+                                   SK_NATIVE_R_IDX,
+                                   SK_NATIVE_G_IDX,
+                                   SK_NATIVE_B_IDX>(a, r, g, b);
+        case SkCanvas::kBGRA_Premul_Config8888:
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            return pack_config8888<3, 2, 1, 0>(a, r, g, b);
+        case SkCanvas::kRGBA_Premul_Config8888:
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            return pack_config8888<3, 0, 1, 2>(a, r, g, b);
+        default:
+            SkDEBUGFAIL("Unexpected config8888");
+            return 0;
+    }
+}
diff --git a/legacy/src/core/SkConfig8888.h b/legacy/src/core/SkConfig8888.h
new file mode 100644
index 0000000..a891370
--- /dev/null
+++ b/legacy/src/core/SkConfig8888.h
@@ -0,0 +1,89 @@
+
+/*
+ * 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 "SkColorPriv.h"
+
+/**
+ * Converts pixels from one Config8888 to another Config8888
+ */
+void SkConvertConfig8888Pixels(uint32_t* dstPixels,
+                               size_t dstRowBytes,
+                               SkCanvas::Config8888 dstConfig,
+                               const uint32_t* srcPixels,
+                               size_t srcRowBytes,
+                               SkCanvas::Config8888 srcConfig,
+                               int width,
+                               int height);
+
+/**
+ * Packs a, r, g, b, values into byte order specified by config.
+ */
+uint32_t SkPackConfig8888(SkCanvas::Config8888 config,
+                          uint32_t a,
+                          uint32_t r,
+                          uint32_t g,
+                          uint32_t b);
+
+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,
+                                     size_t dstRowBytes,
+                                     SkCanvas::Config8888 dstConfig8888,
+                                     const SkBitmap& srcBmp) {
+    SkASSERT(SkBitmap::kARGB_8888_Config == srcBmp.config());
+    SkAutoLockPixels alp(srcBmp);
+    int w = srcBmp.width();
+    int h = srcBmp.height();
+    size_t srcRowBytes = srcBmp.rowBytes();
+    const uint32_t* srcPixels = reinterpret_cast<uint32_t*>(srcBmp.getPixels());
+
+    SkConvertConfig8888Pixels(dstPixels, dstRowBytes, dstConfig8888, srcPixels, srcRowBytes, SkCanvas::kNative_Premul_Config8888, w, h);
+}
+
+inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp,
+                                     const uint32_t* srcPixels,
+                                     size_t srcRowBytes,
+                                     SkCanvas::Config8888 srcConfig8888) {
+    SkASSERT(SkBitmap::kARGB_8888_Config == dstBmp.config());
+    SkAutoLockPixels alp(dstBmp);
+    int w = dstBmp.width();
+    int h = dstBmp.height();
+    size_t dstRowBytes = dstBmp.rowBytes();
+    uint32_t* dstPixels = reinterpret_cast<uint32_t*>(dstBmp.getPixels());
+
+    SkConvertConfig8888Pixels(dstPixels, dstRowBytes, SkCanvas::kNative_Premul_Config8888, srcPixels, srcRowBytes, srcConfig8888, w, h);
+}
+
+}
diff --git a/legacy/src/core/SkCordic.cpp b/legacy/src/core/SkCordic.cpp
new file mode 100644
index 0000000..8fb60c5
--- /dev/null
+++ b/legacy/src/core/SkCordic.cpp
@@ -0,0 +1,293 @@
+
+/*
+ * 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 "SkCordic.h"
+#include "SkMath.h"
+#include "Sk64.h"
+
+// 0x20000000 equals pi / 4
+const int32_t kATanDegrees[] = { 0x20000000,
+    0x12E4051D, 0x9FB385B, 0x51111D4, 0x28B0D43, 0x145D7E1, 0xA2F61E, 0x517C55,
+    0x28BE53, 0x145F2E, 0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
+    0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
+    0xA, 0x5, 0x2, 0x1 };
+
+const int32_t kFixedInvGain1 = 0x18bde0bb;  // 0.607252935
+
+static void SkCircularRotation(int32_t* x0, int32_t* y0, int32_t* z0) 
+{
+    int32_t t = 0;
+    int32_t x = *x0;
+    int32_t y = *y0;
+    int32_t z = *z0;
+    const int32_t* tanPtr = kATanDegrees;
+   do {
+        int32_t x1 = y >> t;
+        int32_t y1 = x >> t;
+        int32_t tan = *tanPtr++;    
+        if (z >= 0) {
+            x -= x1;
+            y += y1;
+            z -= tan;
+        } else {
+            x += x1;
+            y -= y1;
+            z += tan;
+        }
+   } while (++t < 16); // 30);
+    *x0 = x;
+    *y0 = y;
+    *z0 = z;
+}
+
+SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp)
+{
+    int32_t scaledRadians = radians * 0x28be;   // scale radians to 65536 / PI()
+    int quadrant = scaledRadians >> 30;
+    quadrant += 1;
+    if (quadrant & 2) 
+        scaledRadians = -scaledRadians + 0x80000000;
+    /* |a| <= 90 degrees as a 1.31 number */
+    SkFixed sin = 0;
+    SkFixed cos = kFixedInvGain1;
+    SkCircularRotation(&cos, &sin, &scaledRadians);
+    Sk64 scaled;
+    scaled.setMul(sin, 0x6488d);
+    sin = scaled.fHi;
+    scaled.setMul(cos, 0x6488d);
+    if (quadrant & 2)
+        scaled.fHi = - scaled.fHi;
+    *cosp = scaled.fHi;
+    return sin;
+}
+
+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) 
+{
+    int32_t x = *x0;
+    int32_t y = *y0;
+    int32_t z = 0;
+    int32_t t = 0;
+    const int32_t* tanPtr = kATanDegrees;
+   do {
+        int32_t x1 = y >> t;
+        int32_t y1 = x >> t;
+        int32_t tan = *tanPtr++;    
+        if (y < vecMode) {
+            x -= x1;
+            y += y1;
+            z -= tan;
+        } else {
+            x += x1;
+            y -= y1;
+            z += tan;
+        }
+   } while (++t < 16);  // 30
+    Sk64 scaled;
+    scaled.setMul(z, 0x6488d); // scale back into the SkScalar space (0x100000000/0x28be)
+   return scaled.fHi;
+}
+
+SkFixed SkCordicASin(SkFixed a) {
+    int32_t sign = SkExtractSign(a);
+    int32_t z = SkFixedAbs(a);
+    if (z >= SK_Fixed1)
+        return SkApplySign(SK_FixedPI>>1, sign);
+    int32_t x = kFixedInvGain1;
+    int32_t y = 0;
+    z *= 0x28be;
+    z = SkCircularVector(&y, &x, z);
+    z = SkApplySign(z, ~sign);
+    return z;
+}
+
+SkFixed SkCordicACos(SkFixed a) {
+    int32_t z = SkCordicASin(a);
+    z = (SK_FixedPI>>1) - z;
+    return z;
+}
+
+SkFixed SkCordicATan2(SkFixed y, SkFixed x) {
+    if ((x | y) == 0)
+        return 0;
+    int32_t xsign = SkExtractSign(x);
+    x = SkFixedAbs(x);
+    int32_t result = SkCircularVector(&y, &x, 0);
+    if (xsign) {
+        int32_t rsign = SkExtractSign(result);
+        if (y == 0)
+            rsign = 0;
+        SkFixed pi = SkApplySign(SK_FixedPI, rsign);
+        result = pi - result;
+    }
+    return result;
+}
+
+const int32_t kATanHDegrees[] = { 
+    0x1661788D, 0xA680D61, 0x51EA6FC, 0x28CBFDD, 0x1460E34,
+    0xA2FCE8, 0x517D2E, 0x28BE6E, 0x145F32,
+    0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
+    0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
+    0xA, 0x5, 0x2, 0x1 };
+
+const int32_t kFixedInvGain2 = 0x31330AAA;  // 1.207534495
+
+static void SkHyperbolic(int32_t* x0, int32_t* y0, int32_t* z0, int mode) 
+{
+    int32_t t = 1;
+    int32_t x = *x0;
+    int32_t y = *y0;
+    int32_t z = *z0;
+    const int32_t* tanPtr = kATanHDegrees;
+    int k = -3;
+    do {
+        int32_t x1 = y >> t;
+        int32_t y1 = x >> t;
+        int32_t tan = *tanPtr++;    
+        int count = 2 + (k >> 31);
+        if (++k == 1)
+            k = -2;
+        do {
+            if (((y >> 31) & mode) | ~((z >> 31) | mode)) {
+                x += x1;
+                y += y1;
+                z -= tan;
+            } else {
+                x -= x1;
+                y -= y1;
+                z += tan;
+            }
+        } while (--count);
+    } while (++t < 30);
+    *x0 = x;
+    *y0 = y;
+    *z0 = z;
+}
+
+SkFixed SkCordicLog(SkFixed a) {
+    a *= 0x28be;
+    int32_t x = a + 0x28BE60DB; // 1.0
+    int32_t y = a - 0x28BE60DB;
+    int32_t z = 0;
+    SkHyperbolic(&x, &y, &z, -1);
+    Sk64 scaled;
+    scaled.setMul(z, 0x6488d);
+    z = scaled.fHi;
+    return z << 1;
+}
+
+SkFixed SkCordicExp(SkFixed a) {
+    int32_t cosh = kFixedInvGain2;
+    int32_t sinh = 0;
+    SkHyperbolic(&cosh, &sinh, &a, 0);
+    return cosh + sinh;
+}
+
+#ifdef SK_DEBUG
+
+#ifdef SK_CAN_USE_FLOAT
+    #include "SkFloatingPoint.h"
+#endif
+
+void SkCordic_UnitTest()
+{
+#if defined(SK_SUPPORT_UNITTEST) && defined(SK_CAN_USE_FLOAT)
+    float val;
+    for (float angle = -720; angle < 720; angle += 30) {
+        float radian = angle * 3.1415925358f / 180.0f;
+        SkFixed f_angle = (int) (radian * 65536.0f);
+    // sincos
+        float sine = sinf(radian);
+        float cosine = cosf(radian);
+        SkFixed f_cosine;
+        SkFixed f_sine = SkCordicSinCos(f_angle, &f_cosine);
+        float sine2 = (float) f_sine / 65536.0f;
+        float cosine2 = (float) f_cosine / 65536.0f;
+        float error = fabsf(sine - sine2);
+        if (error > 0.001)
+            SkDebugf("sin error : angle = %g ; sin = %g ; cordic = %g\n", angle, sine, sine2);
+        error = fabsf(cosine - cosine2);
+        if (error > 0.001)
+            SkDebugf("cos error : angle = %g ; cos = %g ; cordic = %g\n", angle, cosine, cosine2);
+    // tan
+        float _tan = tanf(radian);
+        SkFixed f_tan = SkCordicTan(f_angle);
+        float tan2 = (float) f_tan / 65536.0f;
+        error = fabsf(_tan - tan2);
+        if (error > 0.05 && fabsf(_tan) < 1e6)
+            SkDebugf("tan error : angle = %g ; tan = %g ; cordic = %g\n", angle, _tan, tan2);
+    }
+    for (val = -1; val <= 1; val += .1f) {
+        SkFixed f_val = (int) (val * 65536.0f);
+    // asin
+        float arcsine = asinf(val);
+        SkFixed f_arcsine = SkCordicASin(f_val);
+        float arcsine2 = (float) f_arcsine / 65536.0f;
+        float error = fabsf(arcsine - arcsine2);
+        if (error > 0.001)
+            SkDebugf("asin error : val = %g ; asin = %g ; cordic = %g\n", val, arcsine, arcsine2);
+    }
+#if 1
+    for (val = -1; val <= 1; val += .1f) {
+#else
+    val = .5; {
+#endif
+        SkFixed f_val = (int) (val * 65536.0f);
+    // acos
+        float arccos = acosf(val);
+        SkFixed f_arccos = SkCordicACos(f_val);
+        float arccos2 = (float) f_arccos / 65536.0f;
+        float error = fabsf(arccos - arccos2);
+        if (error > 0.001)
+            SkDebugf("acos error : val = %g ; acos = %g ; cordic = %g\n", val, arccos, arccos2);
+    }
+    // atan2
+#if 1
+    for (val = -1000; val <= 1000; val += 500.f) {
+        for (float val2 = -1000; val2 <= 1000; val2 += 500.f) {
+#else
+            val = 0; {
+            float val2 = -1000; {
+#endif
+            SkFixed f_val = (int) (val * 65536.0f);
+            SkFixed f_val2 = (int) (val2 * 65536.0f);
+            float arctan = atan2f(val, val2);
+            SkFixed f_arctan = SkCordicATan2(f_val, f_val2);
+            float arctan2 = (float) f_arctan / 65536.0f;
+            float error = fabsf(arctan - arctan2);
+            if (error > 0.001)
+                SkDebugf("atan2 error : val = %g ; val2 = %g ; atan2 = %g ; cordic = %g\n", val, val2, arctan, arctan2);
+        }
+    }
+    // log
+#if 1
+    for (val = 0.125f; val <= 8.f; val *= 2.0f) {
+#else
+    val = .5; {
+#endif
+        SkFixed f_val = (int) (val * 65536.0f);
+    // acos
+        float log = logf(val);
+        SkFixed f_log = SkCordicLog(f_val);
+        float log2 = (float) f_log / 65536.0f;
+        float error = fabsf(log - log2);
+        if (error > 0.001)
+            SkDebugf("log error : val = %g ; log = %g ; cordic = %g\n", val, log, log2);
+    }
+    // exp
+#endif
+}
+
+#endif
diff --git a/legacy/src/core/SkCordic.h b/legacy/src/core/SkCordic.h
new file mode 100644
index 0000000..b70f987
--- /dev/null
+++ b/legacy/src/core/SkCordic.h
@@ -0,0 +1,29 @@
+
+/*
+ * 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 SkCordic_DEFINED
+#define SkCordic_DEFINED
+
+#include "SkTypes.h"
+#include "SkFixed.h"
+
+SkFixed SkCordicACos(SkFixed a);
+SkFixed SkCordicASin(SkFixed a);
+SkFixed SkCordicATan2(SkFixed y, SkFixed x);
+SkFixed SkCordicExp(SkFixed a);
+SkFixed SkCordicLog(SkFixed a);
+SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp);
+SkFixed SkCordicTan(SkFixed a);
+
+#ifdef SK_DEBUG
+    void SkCordic_UnitTest();
+#endif
+
+#endif // SkCordic 
+
diff --git a/legacy/src/core/SkCoreBlitters.h b/legacy/src/core/SkCoreBlitters.h
new file mode 100644
index 0000000..4947198
--- /dev/null
+++ b/legacy/src/core/SkCoreBlitters.h
@@ -0,0 +1,187 @@
+/*
+ * 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 SkCoreBlitters_DEFINED
+#define SkCoreBlitters_DEFINED
+
+#include "SkBlitter.h"
+#include "SkBlitRow.h"
+
+class SkRasterBlitter : public SkBlitter {
+public:
+    SkRasterBlitter(const SkBitmap& device) : fDevice(device) {}
+
+protected:
+    const SkBitmap& fDevice;
+
+private:
+    typedef SkBlitter INHERITED;
+};
+
+class SkShaderBlitter : public SkRasterBlitter {
+public:
+    SkShaderBlitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkShaderBlitter();
+
+protected:
+    uint32_t    fShaderFlags;
+    SkShader*   fShader;
+
+private:
+    // illegal
+    SkShaderBlitter& operator=(const SkShaderBlitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkA8_Blitter : public SkRasterBlitter {
+public:
+    SkA8_Blitter(const SkBitmap& device, const SkPaint& paint);
+    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 blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+private:
+    unsigned fSrcA;
+
+    // illegal
+    SkA8_Blitter& operator=(const SkA8_Blitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+class SkA8_Shader_Blitter : public SkShaderBlitter {
+public:
+    SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkA8_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&);
+
+private:
+    SkXfermode* fXfermode;
+    SkPMColor*  fBuffer;
+    uint8_t*    fAAExpand;
+
+    // illegal
+    SkA8_Shader_Blitter& operator=(const SkA8_Shader_Blitter&);
+
+    typedef SkShaderBlitter INHERITED;
+};
+
+////////////////////////////////////////////////////////////////
+
+class SkARGB32_Blitter : public SkRasterBlitter {
+public:
+    SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint);
+    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 blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+protected:
+    SkColor                fColor;
+    SkPMColor              fPMColor;
+    SkBlitRow::ColorProc   fColor32Proc;
+
+private:
+    unsigned fSrcA, fSrcR, fSrcG, fSrcB;
+
+    // illegal
+    SkARGB32_Blitter& operator=(const SkARGB32_Blitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+class SkARGB32_Opaque_Blitter : public SkARGB32_Blitter {
+public:
+    SkARGB32_Opaque_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device, paint) { SkASSERT(paint.getAlpha() == 0xFF); }
+    virtual void blitMask(const SkMask&, const SkIRect&);
+
+private:
+    typedef SkARGB32_Blitter INHERITED;
+};
+
+class SkARGB32_Black_Blitter : public SkARGB32_Opaque_Blitter {
+public:
+    SkARGB32_Black_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device, paint) {}
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+
+private:
+    typedef SkARGB32_Opaque_Blitter INHERITED;
+};
+
+class SkARGB32_Shader_Blitter : public SkShaderBlitter {
+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&);
+
+private:
+    SkXfermode*         fXfermode;
+    SkPMColor*          fBuffer;
+    SkBlitRow::Proc32   fProc32;
+    SkBlitRow::Proc32   fProc32Blend;
+
+    // illegal
+    SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&);
+
+    typedef SkShaderBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkA1_Blitter : public SkRasterBlitter {
+public:
+    SkA1_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual void blitH(int x, int y, int width);
+
+private:
+    uint8_t fSrcA;
+
+    // illegal
+    SkA1_Blitter& operator=(const SkA1_Blitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  These return the correct subclass of blitter for their device config.
+
+    Currently, they make the following assumptions about the state of the
+    paint:
+
+    1. If there is an xfermode, there will also be a shader
+    2. If there is a colorfilter, there will be a shader that itself handles
+       calling the filter, so the blitter can always ignore the colorfilter obj
+
+    These pre-conditions must be handled by the caller, in our case
+    SkBlitter::Choose(...)
+ */
+
+extern SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device,
+                                        const SkPaint& paint,
+                                        void* storage, size_t storageSize);
+
+extern SkBlitter* SkBlitter_ChooseD565(const SkBitmap& device,
+                                       const SkPaint& paint,
+                                       void* storage, size_t storageSize);
+
+#endif
+
diff --git a/legacy/src/core/SkCubicClipper.cpp b/legacy/src/core/SkCubicClipper.cpp
new file mode 100644
index 0000000..662591f
--- /dev/null
+++ b/legacy/src/core/SkCubicClipper.cpp
@@ -0,0 +1,161 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkCubicClipper.h"
+#include "SkGeometry.h"
+
+SkCubicClipper::SkCubicClipper() {}
+
+void SkCubicClipper::setClip(const SkIRect& clip) {
+    // conver to scalars, since that's where we'll see the points
+    fClip.set(clip);
+}
+
+
+static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) {
+    SkScalar ycrv[4];
+    ycrv[0] = pts[0].fY - y;
+    ycrv[1] = pts[1].fY - y;
+    ycrv[2] = pts[2].fY - y;
+    ycrv[3] = pts[3].fY - y;
+
+#ifdef NEWTON_RAPHSON    // Quadratic convergence, typically <= 3 iterations.
+    // Initial guess.
+    // TODO(turk): Check for zero denominator? Shouldn't happen unless the curve
+    // is not only monotonic but degenerate.
+#ifdef SK_SCALAR_IS_FLOAT
+    SkScalar t1 = ycrv[0] / (ycrv[0] - ycrv[3]);
+#else  // !SK_SCALAR_IS_FLOAT
+    SkScalar t1 = SkDivBits(ycrv[0], ycrv[0] - ycrv[3], 16);
+#endif  // !SK_SCALAR_IS_FLOAT
+
+    // Newton's iterations.
+    const SkScalar tol = SK_Scalar1 / 16384;  // This leaves 2 fixed noise bits.
+    SkScalar t0;
+    const int maxiters = 5;
+    int iters = 0;
+    bool converged;
+    do {
+        t0 = t1;
+        SkScalar y01   = SkScalarInterp(ycrv[0], ycrv[1], t0);
+        SkScalar y12   = SkScalarInterp(ycrv[1], ycrv[2], t0);
+        SkScalar y23   = SkScalarInterp(ycrv[2], ycrv[3], t0);
+        SkScalar y012  = SkScalarInterp(y01,  y12,  t0);
+        SkScalar y123  = SkScalarInterp(y12,  y23,  t0);
+        SkScalar y0123 = SkScalarInterp(y012, y123, t0);
+        SkScalar yder  = (y123 - y012) * 3;
+        // TODO(turk): check for yder==0: horizontal.
+#ifdef SK_SCALAR_IS_FLOAT
+        t1 -= y0123 / yder;
+#else  // !SK_SCALAR_IS_FLOAT
+        t1 -= SkDivBits(y0123, yder, 16);
+#endif  // !SK_SCALAR_IS_FLOAT
+        converged = SkScalarAbs(t1 - t0) <= tol;  // NaN-safe
+        ++iters;
+    } while (!converged && (iters < maxiters));
+    *t = t1;                  // Return the result.
+
+    // The result might be valid, even if outside of the range [0, 1], but
+    // we never evaluate a Bezier outside this interval, so we return false.
+    if (t1 < 0 || t1 > SK_Scalar1)
+        return false;         // This shouldn't happen, but check anyway.
+    return converged;
+
+#else  // BISECTION    // Linear convergence, typically 16 iterations.
+
+    // Check that the endpoints straddle zero.
+    SkScalar tNeg, tPos;    // Negative and positive function parameters.
+    if (ycrv[0] < 0) {
+        if (ycrv[3] < 0)
+            return false;
+        tNeg = 0;
+        tPos = SK_Scalar1;
+    } else if (ycrv[0] > 0) {
+        if (ycrv[3] > 0)
+            return false;
+        tNeg = SK_Scalar1;
+        tPos = 0;
+    } else {
+        *t = 0;
+        return true;
+    }
+
+    const SkScalar tol = SK_Scalar1 / 65536;  // 1 for fixed, 1e-5 for float.
+    int iters = 0;
+    do {
+        SkScalar tMid = (tPos + tNeg) / 2;
+        SkScalar y01   = SkScalarInterp(ycrv[0], ycrv[1], tMid);
+        SkScalar y12   = SkScalarInterp(ycrv[1], ycrv[2], tMid);
+        SkScalar y23   = SkScalarInterp(ycrv[2], ycrv[3], tMid);
+        SkScalar y012  = SkScalarInterp(y01,     y12,     tMid);
+        SkScalar y123  = SkScalarInterp(y12,     y23,     tMid);
+        SkScalar y0123 = SkScalarInterp(y012,    y123,    tMid);
+        if (y0123 == 0) {
+            *t = tMid;
+            return true;
+        }
+        if (y0123 < 0)  tNeg = tMid;
+        else            tPos = tMid;
+        ++iters;
+    } while (!(SkScalarAbs(tPos - tNeg) <= tol));   // Nan-safe
+
+    *t = (tNeg + tPos) / 2;
+    return true;
+#endif  // BISECTION
+}
+
+
+bool SkCubicClipper::clipCubic(const SkPoint srcPts[4], SkPoint dst[4]) {
+    bool reverse;
+
+    // we need the data to be monotonically descending in Y
+    if (srcPts[0].fY > srcPts[3].fY) {
+        dst[0] = srcPts[3];
+        dst[1] = srcPts[2];
+        dst[2] = srcPts[1];
+        dst[3] = srcPts[0];
+        reverse = true;
+    } else {
+        memcpy(dst, srcPts, 4 * sizeof(SkPoint));
+        reverse = false;
+    }
+
+    // are we completely above or below
+    const SkScalar ctop = fClip.fTop;
+    const SkScalar cbot = fClip.fBottom;
+    if (dst[3].fY <= ctop || dst[0].fY >= cbot) {
+        return false;
+    }
+
+    SkScalar t;
+    SkPoint tmp[7]; // for SkChopCubicAt
+
+    // are we partially above
+    if (dst[0].fY < ctop && chopMonoCubicAtY(dst, ctop, &t)) {
+        SkChopCubicAt(dst, tmp, t);
+        dst[0] = tmp[3];
+        dst[1] = tmp[4];
+        dst[2] = tmp[5];
+    }
+
+    // are we partially below
+    if (dst[3].fY > cbot && chopMonoCubicAtY(dst, cbot, &t)) {
+        SkChopCubicAt(dst, tmp, t);
+        dst[1] = tmp[1];
+        dst[2] = tmp[2];
+        dst[3] = tmp[3];
+    }
+
+    if (reverse) {
+        SkTSwap<SkPoint>(dst[0], dst[3]);
+        SkTSwap<SkPoint>(dst[1], dst[2]);
+    }
+    return true;
+}
+
diff --git a/legacy/src/core/SkCubicClipper.h b/legacy/src/core/SkCubicClipper.h
new file mode 100644
index 0000000..c52eabe
--- /dev/null
+++ b/legacy/src/core/SkCubicClipper.h
@@ -0,0 +1,34 @@
+
+/*
+ * 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 SkCubicClipper_DEFINED
+#define SkCubicClipper_DEFINED
+
+#include "SkPoint.h"
+#include "SkRect.h"
+
+/** This class is initialized with a clip rectangle, and then can be fed cubics,
+    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 SkCubicClipper {
+public:
+    SkCubicClipper();
+
+    void setClip(const SkIRect& clip);
+
+    bool clipCubic(const SkPoint src[4], SkPoint dst[4]);
+
+private:
+    SkRect      fClip;
+};
+
+#endif  // SkCubicClipper_DEFINED
diff --git a/legacy/src/core/SkData.cpp b/legacy/src/core/SkData.cpp
new file mode 100644
index 0000000..f9f4043
--- /dev/null
+++ b/legacy/src/core/SkData.cpp
@@ -0,0 +1,103 @@
+
+/*
+ * 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 "SkData.h"
+
+SkData::SkData(const void* ptr, size_t size, ReleaseProc proc, void* context) {
+    fPtr = ptr;
+    fSize = size;
+    fReleaseProc = proc;
+    fReleaseProcContext = context;
+}
+
+SkData::~SkData() {
+    if (fReleaseProc) {
+        fReleaseProc(fPtr, fSize, fReleaseProcContext);
+    }
+}
+
+size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const {
+    size_t available = fSize;
+    if (offset >= available || 0 == length) {
+        return 0;
+    }
+    available -= offset;
+    if (length > available) {
+        length = available;
+    }
+    SkASSERT(length > 0);
+
+    memcpy(buffer, this->bytes() + offset, length);
+    return length;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkData* SkData::NewEmpty() {
+    static SkData* gEmptyRef;
+    if (NULL == gEmptyRef) {
+        gEmptyRef = new SkData(NULL, 0, NULL, NULL);
+    }
+    gEmptyRef->ref();
+    return gEmptyRef;
+}
+
+// assumes fPtr was allocated via sk_malloc
+static void sk_free_releaseproc(const void* ptr, size_t, void*) {
+    sk_free((void*)ptr);
+}
+
+SkData* SkData::NewFromMalloc(const void* data, size_t length) {
+    return new SkData(data, length, sk_free_releaseproc, NULL);
+}
+
+SkData* SkData::NewWithCopy(const void* data, size_t length) {
+    if (0 == length) {
+        return SkData::NewEmpty();
+    }
+
+    void* copy = sk_malloc_throw(length); // balanced in sk_free_releaseproc
+    memcpy(copy, data, length);
+    return new SkData(copy, length, sk_free_releaseproc, NULL);
+}
+
+SkData* SkData::NewWithProc(const void* data, size_t length,
+                            ReleaseProc proc, void* context) {
+    return new SkData(data, length, proc, context);
+}
+
+// assumes context is a SkData
+static void sk_dataref_releaseproc(const void*, size_t, void* context) {
+    SkData* src = reinterpret_cast<SkData*>(context);
+    src->unref();
+}
+
+SkData* SkData::NewSubset(const SkData* src, size_t offset, size_t length) {
+    /*
+        We could, if we wanted/need to, just make a deep copy of src's data,
+        rather than referencing it. This would duplicate the storage (of the
+        subset amount) but would possibly allow src to go out of scope sooner.
+     */
+
+    size_t available = src->size();
+    if (offset >= available || 0 == length) {
+        return SkData::NewEmpty();
+    }
+    available -= offset;
+    if (length > available) {
+        length = available;
+    }
+    SkASSERT(length > 0);
+
+    src->ref(); // this will be balanced in sk_dataref_releaseproc
+    return new SkData(src->bytes() + offset, length, sk_dataref_releaseproc,
+                         const_cast<SkData*>(src));
+}
+
diff --git a/legacy/src/core/SkDebug.cpp b/legacy/src/core/SkDebug.cpp
new file mode 100644
index 0000000..4b33836
--- /dev/null
+++ b/legacy/src/core/SkDebug.cpp
@@ -0,0 +1,51 @@
+
+/*
+ * 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 "SkTypes.h"
+
+#ifdef SK_DEBUG
+
+int8_t SkToS8(long x)
+{
+    SkASSERT((int8_t)x == x);
+    return (int8_t)x;
+}
+
+uint8_t SkToU8(size_t x)
+{
+    SkASSERT((uint8_t)x == x);
+    return (uint8_t)x;
+}
+
+int16_t SkToS16(long x)
+{
+    SkASSERT((int16_t)x == x);
+    return (int16_t)x;
+}
+
+uint16_t SkToU16(size_t x)
+{
+    SkASSERT((uint16_t)x == x);
+    return (uint16_t)x;
+}
+
+int32_t SkToS32(long x)
+{
+    SkASSERT((int32_t)x == x);
+    return (int32_t)x;
+}
+
+uint32_t SkToU32(size_t x)
+{
+    SkASSERT((uint32_t)x == x);
+    return (uint32_t)x;
+}
+
+#endif
+
diff --git a/legacy/src/core/SkDeque.cpp b/legacy/src/core/SkDeque.cpp
new file mode 100644
index 0000000..385b48d
--- /dev/null
+++ b/legacy/src/core/SkDeque.cpp
@@ -0,0 +1,250 @@
+
+/*
+ * 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 "SkDeque.h"
+
+#define INIT_ELEM_COUNT 1  // should we let the caller set this?
+
+struct SkDeque::Head {
+    Head*   fNext;
+    Head*   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
+
+    char*       start() { return (char*)(this + 1); }
+    const char* start() const { return (const char*)(this + 1); }
+
+    void init(size_t size) {
+        fNext   = fPrev = NULL;
+        fBegin  = fEnd = NULL;
+        fStop   = (char*)this + size;
+    }
+};
+
+SkDeque::SkDeque(size_t elemSize)
+        : fElemSize(elemSize), fInitialStorage(NULL), fCount(0) {
+    fFront = fBack = NULL;
+}
+
+SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize)
+        : fElemSize(elemSize), fInitialStorage(storage), fCount(0) {
+    SkASSERT(storageSize == 0 || storage != NULL);
+
+    if (storageSize >= sizeof(Head) + elemSize) {
+        fFront = (Head*)storage;
+        fFront->init(storageSize);
+    } else {
+        fFront = NULL;
+    }
+    fBack = fFront;
+}
+
+SkDeque::~SkDeque() {
+    Head* head = fFront;
+    Head* initialHead = (Head*)fInitialStorage;
+
+    while (head) {
+        Head* next = head->fNext;
+        if (head != initialHead) {
+            sk_free(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
+    }
+
+    Head*   first = fFront;
+    char*   begin;
+
+    if (NULL == first->fBegin) {
+    INIT_CHUNK:
+        first->fEnd = first->fStop;
+        begin = first->fStop - fElemSize;
+    } else {
+        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;
+            goto INIT_CHUNK;
+        }
+    }
+
+    first->fBegin = 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
+    }
+
+    Head*   last = fBack;
+    char*   end;
+
+    if (NULL == last->fBegin) {
+    INIT_CHUNK:
+        last->fBegin = last->start();
+        end = last->fBegin + fElemSize;
+    } else {
+        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;
+            goto INIT_CHUNK;
+        }
+    }
+
+    last->fEnd = end;
+    return end - fElemSize;
+}
+
+void SkDeque::pop_front() {
+    SkASSERT(fCount > 0);
+    fCount -= 1;
+
+    Head*   first = fFront;
+
+    SkASSERT(first != NULL);
+
+    if (first->fBegin == NULL) {  // we were marked empty from before
+        first = first->fNext;
+        first->fPrev = NULL;
+        sk_free(fFront);
+        fFront = first;
+        SkASSERT(first != NULL);    // else we popped too far
+    }
+
+    char* begin = first->fBegin + fElemSize;
+    SkASSERT(begin <= first->fEnd);
+
+    if (begin < fFront->fEnd) {
+        first->fBegin = begin;
+    } else {
+        first->fBegin = first->fEnd = NULL;  // mark as empty
+    }
+}
+
+void SkDeque::pop_back() {
+    SkASSERT(fCount > 0);
+    fCount -= 1;
+
+    Head* last = fBack;
+
+    SkASSERT(last != NULL);
+
+    if (last->fEnd == NULL) {  // we were marked empty from before
+        last = last->fPrev;
+        last->fNext = NULL;
+        sk_free(fBack);
+        fBack = last;
+        SkASSERT(last != NULL);  // else we popped too far
+    }
+
+    char* end = last->fEnd - fElemSize;
+    SkASSERT(end >= last->fBegin);
+
+    if (end > last->fBegin) {
+        last->fEnd = end;
+    } else {
+        last->fBegin = last->fEnd = NULL;    // mark as empty
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDeque::F2BIter::F2BIter() : fHead(NULL), fPos(NULL), fElemSize(0) {}
+
+SkDeque::F2BIter::F2BIter(const SkDeque& d) {
+    this->reset(d);
+}
+
+void* SkDeque::F2BIter::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
+            do {
+                fHead = fHead->fNext;
+            } while (fHead != NULL && fHead->fBegin == NULL);
+            next = fHead ? fHead->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;
+    }
+    fPos = fHead ? fHead->fBegin : NULL;
+}
diff --git a/legacy/src/core/SkDevice.cpp b/legacy/src/core/SkDevice.cpp
new file mode 100644
index 0000000..f1da2ef
--- /dev/null
+++ b/legacy/src/core/SkDevice.cpp
@@ -0,0 +1,385 @@
+
+/*
+ * 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 "SkDevice.h"
+#include "SkDraw.h"
+#include "SkImageFilter.h"
+#include "SkMetaData.h"
+#include "SkRect.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {
+    fOrigin.setZero();
+    fMetaData = NULL;
+}
+
+SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque) {
+    fOrigin.setZero();
+    fMetaData = NULL;
+
+    fBitmap.setConfig(config, width, height);
+    fBitmap.allocPixels();
+    fBitmap.setIsOpaque(isOpaque);
+    if (!isOpaque) {
+        fBitmap.eraseColor(0);
+    }
+}
+
+SkDevice::~SkDevice() {
+    delete fMetaData;
+}
+
+SkDevice* SkDevice::createCompatibleDevice(SkBitmap::Config config,
+                                           int width, int height,
+                                           bool isOpaque) {
+    return this->onCreateCompatibleDevice(config, width, height,
+                                          isOpaque, kGeneral_Usage);
+}
+
+SkDevice* SkDevice::createCompatibleDeviceForSaveLayer(SkBitmap::Config config,
+                                                       int width, int height,
+                                                       bool isOpaque) {
+    return this->onCreateCompatibleDevice(config, width, height,
+                                          isOpaque, kSaveLayer_Usage);
+}
+
+SkDevice* SkDevice::onCreateCompatibleDevice(SkBitmap::Config config,
+                                             int width, int height,
+                                             bool isOpaque,
+                                             Usage usage) {
+    return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque));
+}
+
+SkMetaData& SkDevice::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 SkDevice::lockPixels() {
+    if (fBitmap.lockPixelsAreWritable()) {
+        fBitmap.lockPixels();
+    }
+}
+
+void SkDevice::unlockPixels() {
+    if (fBitmap.lockPixelsAreWritable()) {
+        fBitmap.unlockPixels();
+    }
+}
+
+const SkBitmap& SkDevice::accessBitmap(bool changePixels) {
+    const SkBitmap& bitmap = this->onAccessBitmap(&fBitmap);
+    if (changePixels) {
+        bitmap.notifyPixelsChanged();
+    }
+    return bitmap;
+}
+
+void SkDevice::getGlobalBounds(SkIRect* bounds) const {
+    if (bounds) {
+        bounds->setXYWH(fOrigin.x(), fOrigin.y(),
+                        fBitmap.width(), fBitmap.height());
+    }
+}
+
+void SkDevice::clear(SkColor color) {
+    fBitmap.eraseColor(color);
+}
+
+const SkBitmap& SkDevice::onAccessBitmap(SkBitmap* bitmap) {return *bitmap;}
+
+void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region,
+                             const SkClipStack& clipStack) {
+}
+
+bool SkDevice::filterImage(SkImageFilter*, const SkBitmap& src,
+                           const SkMatrix& ctm,
+                           SkBitmap* result, SkIPoint* offset) {
+    return false;
+}
+
+bool SkDevice::allowImageFilter(SkImageFilter*) {
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y,
+                          SkCanvas::Config8888 config8888) {
+    if (SkBitmap::kARGB_8888_Config != bitmap->config() ||
+        NULL != bitmap->getTexture()) {
+        return false;
+    }
+
+    const SkBitmap& src = this->accessBitmap(false);
+
+    SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap->width(),
+                                              bitmap->height());
+    SkIRect devbounds = SkIRect::MakeWH(src.width(), src.height());
+    if (!srcRect.intersect(devbounds)) {
+        return false;
+    }
+
+    SkBitmap tmp;
+    SkBitmap* bmp;
+    if (bitmap->isNull()) {
+        tmp.setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(),
+                                                   bitmap->height());
+        if (!tmp.allocPixels()) {
+            return false;
+        }
+        bmp = &tmp;
+    } else {
+        bmp = bitmap;
+    }
+
+    SkIRect subrect = srcRect;
+    subrect.offset(-x, -y);
+    SkBitmap bmpSubset;
+    bmp->extractSubset(&bmpSubset, subrect);
+
+    bool result = this->onReadPixels(bmpSubset,
+                                     srcRect.fLeft,
+                                     srcRect.fTop,
+                                     config8888);
+    if (result && bmp == &tmp) {
+        tmp.swap(*bitmap);
+    }
+    return result;
+}
+
+#ifdef SK_CPU_LENDIAN
+    #if   24 == SK_A32_SHIFT && 16 == SK_R32_SHIFT && \
+           8 == SK_G32_SHIFT &&  0 == SK_B32_SHIFT
+        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
+            SkCanvas::kBGRA_Premul_Config8888;
+    #elif 24 == SK_A32_SHIFT &&  0 == SK_R32_SHIFT && \
+           8 == SK_G32_SHIFT && 16 == SK_B32_SHIFT
+        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
+            SkCanvas::kRGBA_Premul_Config8888;
+    #else
+        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
+            (SkCanvas::Config8888) -1;
+    #endif
+#else
+    #if    0 == SK_A32_SHIFT &&   8 == SK_R32_SHIFT && \
+          16 == SK_G32_SHIFT &&  24 == SK_B32_SHIFT
+        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
+            SkCanvas::kBGRA_Premul_Config8888;
+    #elif  0 == SK_A32_SHIFT &&  24 == SK_R32_SHIFT && \
+          16 == SK_G32_SHIFT &&   8 == SK_B32_SHIFT
+        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
+            SkCanvas::kRGBA_Premul_Config8888;
+    #else
+        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
+            (SkCanvas::Config8888) -1;
+    #endif
+#endif
+
+#include <SkConfig8888.h>
+
+bool SkDevice::onReadPixels(const SkBitmap& bitmap,
+                            int x, int y,
+                            SkCanvas::Config8888 config8888) {
+    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())));
+
+    SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap.width(),
+                                              bitmap.height());
+    const SkBitmap& src = this->accessBitmap(false);
+
+    SkBitmap subset;
+    if (!src.extractSubset(&subset, srcRect)) {
+        return false;
+    }
+    if (SkBitmap::kARGB_8888_Config != subset.config()) {
+        // It'd be preferable to do this directly to bitmap.
+        subset.copyTo(&subset, SkBitmap::kARGB_8888_Config);
+    }
+    SkAutoLockPixels alp(bitmap);
+    uint32_t* bmpPixels = reinterpret_cast<uint32_t*>(bitmap.getPixels());
+    SkCopyBitmapToConfig8888(bmpPixels, bitmap.rowBytes(), config8888, subset);
+    return true;
+}
+
+void SkDevice::writePixels(const SkBitmap& bitmap,
+                           int x, int y,
+                           SkCanvas::Config8888 config8888) {
+    if (bitmap.isNull() || bitmap.getTexture()) {
+        return;
+    }
+    const SkBitmap* sprite = &bitmap;
+    // check whether we have to handle a config8888 that doesn't match SkPMColor
+    if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
+        SkCanvas::kNative_Premul_Config8888 != config8888 &&
+        kPMColorAlias != config8888) {
+
+        // We're going to have to convert from a config8888 to the native config
+        // First we clip to the device bounds.
+        SkBitmap dstBmp = this->accessBitmap(true);
+        SkIRect spriteRect = SkIRect::MakeXYWH(x, y,
+                                               bitmap.width(), bitmap.height());
+        SkIRect devRect = SkIRect::MakeWH(dstBmp.width(), dstBmp.height());
+        if (!spriteRect.intersect(devRect)) {
+            return;
+        }
+
+        // write directly to the device if it has pixels and is SkPMColor
+        bool drawSprite;
+        if (SkBitmap::kARGB_8888_Config == dstBmp.config() && !dstBmp.isNull()) {
+            // we can write directly to the dst when doing the conversion
+            dstBmp.extractSubset(&dstBmp, spriteRect);
+            drawSprite = false;
+        } else {
+            // we convert to a temporary bitmap and draw that as a sprite
+            dstBmp.setConfig(SkBitmap::kARGB_8888_Config,
+                             spriteRect.width(),
+                             spriteRect.height());
+            if (!dstBmp.allocPixels()) {
+                return;
+            }
+            drawSprite = true;
+        }
+
+        // copy pixels to dstBmp and convert from config8888 to native config.
+        SkAutoLockPixels alp(bitmap);
+        uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x,
+                                               spriteRect.fTop - y);
+        SkCopyConfig8888ToBitmap(dstBmp,
+                                 srcPixels,
+                                 bitmap.rowBytes(),
+                                 config8888);
+
+        if (drawSprite) {
+            // we've clipped the sprite when we made a copy
+            x = spriteRect.fLeft;
+            y = spriteRect.fTop;
+            sprite = &dstBmp;
+        } else {
+            return;
+        }
+    }
+
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    SkCanvas canvas(this);
+    canvas.drawSprite(*sprite, x, y, &paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+    draw.drawPaint(paint);
+}
+
+void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+                              const SkPoint pts[], const SkPaint& paint) {
+    draw.drawPoints(mode, count, pts, paint);
+}
+
+void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
+                            const SkPaint& paint) {
+    draw.drawRect(r, paint);
+}
+
+void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
+                        const SkPaint& paint, const SkMatrix* prePathMatrix,
+                        bool pathIsMutable) {
+    draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
+}
+
+void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                          const SkIRect* srcRect,
+                          const SkMatrix& matrix, const SkPaint& paint) {
+    SkBitmap        tmp;    // storage if we need a subset of bitmap
+    const SkBitmap* bitmapPtr = &bitmap;
+
+    if (srcRect) {
+        if (!bitmap.extractSubset(&tmp, *srcRect)) {
+            return;     // extraction failed
+        }
+        bitmapPtr = &tmp;
+    }
+    draw.drawBitmap(*bitmapPtr, matrix, paint);
+}
+
+void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                              int x, int y, const SkPaint& paint) {
+    draw.drawSprite(bitmap, x, y, paint);
+}
+
+void SkDevice::drawText(const SkDraw& draw, const void* text, size_t len,
+                            SkScalar x, SkScalar y, const SkPaint& paint) {
+    draw.drawText((const char*)text, len, x, y, paint);
+}
+
+void SkDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
+                               const SkScalar xpos[], SkScalar y,
+                               int scalarsPerPos, const SkPaint& paint) {
+    draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
+}
+
+void SkDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+                                  size_t len, const SkPath& path,
+                                  const SkMatrix* matrix,
+                                  const SkPaint& paint) {
+    draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+void SkDevice::drawPosTextOnPath(const SkDraw& draw, const void* text, size_t len,
+                                     const SkPoint pos[], const SkPaint& paint,
+                                     const SkPath& path, const SkMatrix* matrix) {
+    draw.drawPosTextOnPath((const char*)text, len, pos, paint, path, matrix);
+}
+#endif
+
+void SkDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                                int vertexCount,
+                                const SkPoint verts[], const SkPoint textures[],
+                                const SkColor colors[], SkXfermode* xmode,
+                                const uint16_t indices[], int indexCount,
+                                const SkPaint& paint) {
+    draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
+                      indices, indexCount, paint);
+}
+
+void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device,
+                              int x, int y, const SkPaint& paint) {
+    const SkBitmap& src = device->accessBitmap(false);
+    draw.drawSprite(src, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
+    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
+        // we're cool with the paint as is
+        return false;
+    }
+
+    if (SkBitmap::kARGB_8888_Config != fBitmap.config() ||
+        paint.getRasterizer() ||
+        paint.getPathEffect() ||
+        paint.isFakeBoldText() ||
+        paint.getStyle() != SkPaint::kFill_Style ||
+        !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) {
+        // turn off lcd
+        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
+        flags->fHinting = paint.getHinting();
+        return true;
+    }
+    // we're cool with the paint as is
+    return false;
+}
+
diff --git a/legacy/src/core/SkDeviceProfile.cpp b/legacy/src/core/SkDeviceProfile.cpp
new file mode 100644
index 0000000..2c2cb88
--- /dev/null
+++ b/legacy/src/core/SkDeviceProfile.cpp
@@ -0,0 +1,72 @@
+
+
+#include "SkDeviceProfile.h"
+
+#define DEFAULT_GAMMAEXP        2.2
+#define DEFAULT_CONTRASTSCALE   0.5
+#define DEFAULT_LCDCONFIG       SkDeviceProfile::kNone_LCDConfig
+#define DEFAULT_FONTHINTLEVEL   SkDeviceProfile::kSlight_FontHintLevel
+
+static float pin(float value, float min, float max) {
+    if (value < min) {
+        value = min;
+    } else if (value > max) {
+        value = max;
+    }
+    return value;
+}
+
+SkDeviceProfile::SkDeviceProfile(float gammaExp, float contrast,
+                                 LCDConfig config, FontHintLevel level) {
+    fGammaExponent = pin(gammaExp, 0, 10);
+    fContrastScale = pin(contrast, 0, 1);
+    fLCDConfig = config;
+    fFontHintLevel = level;
+}
+
+void SkDeviceProfile::generateTableForLuminanceByte(U8CPU lumByte,
+                                                    uint8_t table[256]) const {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDeviceProfile* SkDeviceProfile::Create(float gammaExp,
+                                         float contrast,
+                                         LCDConfig config,
+                                         FontHintLevel level) {
+    return SkNEW_ARGS(SkDeviceProfile, (gammaExp, contrast, config, level));
+}
+
+static SkMutex gMutex;
+static SkDeviceProfile* gDefaultProfile;
+static SkDeviceProfile* gGlobalProfile;
+
+SkDeviceProfile* SkDeviceProfile::GetDefault() {
+    SkAutoMutexAcquire amc(gMutex);
+
+    if (NULL == gDefaultProfile) {
+        gDefaultProfile = SkDeviceProfile::Create(DEFAULT_GAMMAEXP,
+                                                  DEFAULT_CONTRASTSCALE,
+                                                  DEFAULT_LCDCONFIG,
+                                                  DEFAULT_FONTHINTLEVEL);
+    }
+    return gDefaultProfile;
+}
+
+SkDeviceProfile* SkDeviceProfile::RefGlobal() {
+    SkAutoMutexAcquire amc(gMutex);
+
+    if (NULL == gGlobalProfile) {
+        gGlobalProfile = SkDeviceProfile::GetDefault();
+    }
+    gGlobalProfile->ref();
+    return gGlobalProfile;
+}
+    
+void SkDeviceProfile::SetGlobal(SkDeviceProfile* profile) {
+    SkAutoMutexAcquire amc(gMutex);
+
+    SkRefCnt_SafeAssign(gGlobalProfile, profile);
+}
+
+
diff --git a/legacy/src/core/SkDither.cpp b/legacy/src/core/SkDither.cpp
new file mode 100644
index 0000000..bdd8c2f
--- /dev/null
+++ b/legacy/src/core/SkDither.cpp
@@ -0,0 +1,56 @@
+
+/*
+ * 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 "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 },
+    { 60, 28, 52, 20, 62, 30, 54, 22 },
+    { 3,  35, 11, 43, 1,  33, 9,  41 },
+    { 51, 19, 59, 27, 49, 17, 57, 25 },
+    { 15, 47, 7,  39, 13, 45, 5,  37 },
+    { 63, 31, 55, 23, 61, 29, 53, 21 }
+
+    The 4444 version only needs 4 bits, and given that we can reduce its size
+    since the other 4x4 sub pieces all look the same once we truncate the bits.
+
+    The 565 version only needs 3 bits for red/blue, and only 2 bits for green.
+    For simplicity, we store 3 bits, and have the dither macros for green know
+    this, and they shift the dither value down by 1 to make it 2 bits.
+ */
+
+#ifdef ENABLE_DITHER_MATRIX_4X4
+
+const uint8_t gDitherMatrix_4Bit_4X4[4][4] = {
+    {  0,  8,  2, 10 },
+    { 12,  4, 14,  6 },
+    {  3, 11,  1,  9 },
+    { 15,  7, 13,  5 }
+};
+
+const uint8_t gDitherMatrix_3Bit_4X4[4][4] = {
+    {  0,  4,  1,  5 },
+    {  6,  2,  7,  3 },
+    {  1,  5,  0,  4 },
+    {  7,  3,  6,  2 }
+};
+
+#else   // used packed shorts for a scanlines worth of dither values
+
+const uint16_t gDitherMatrix_4Bit_16[4] = {
+    0xA280, 0x6E4C, 0x91B3, 0x5D7F
+};
+
+const uint16_t gDitherMatrix_3Bit_16[4] = {
+    0x5140, 0x3726, 0x4051, 0x2637
+};
+
+#endif
+
diff --git a/legacy/src/core/SkDraw.cpp b/legacy/src/core/SkDraw.cpp
new file mode 100644
index 0000000..23a6d59
--- /dev/null
+++ b/legacy/src/core/SkDraw.cpp
@@ -0,0 +1,2639 @@
+
+/*
+ * 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 "SkDraw.h"
+#include "SkBlitter.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkFixed.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkPathEffect.h"
+#include "SkRasterClip.h"
+#include "SkRasterizer.h"
+#include "SkScan.h"
+#include "SkShader.h"
+#include "SkStroke.h"
+#include "SkTemplatesPriv.h"
+#include "SkTLazy.h"
+#include "SkUtils.h"
+
+#include "SkAutoKern.h"
+#include "SkBitmapProcShader.h"
+#include "SkDrawProcs.h"
+
+//#define TRACE_BITMAP_DRAWS
+
+#define kBlitterStorageLongCount    (sizeof(SkBitmapProcShader) >> 2)
+
+/** Helper for allocating small blitters on the stack.
+ */
+class SkAutoBlitterChoose : SkNoncopyable {
+public:
+    SkAutoBlitterChoose() {
+        fBlitter = NULL;
+    }
+    SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix,
+                        const SkPaint& paint) {
+        fBlitter = SkBlitter::Choose(device, matrix, paint,
+                                     fStorage, sizeof(fStorage));
+    }
+    
+    ~SkAutoBlitterChoose();
+
+    SkBlitter*  operator->() { return fBlitter; }
+    SkBlitter*  get() const { return fBlitter; }
+
+    void choose(const SkBitmap& device, const SkMatrix& matrix,
+                const SkPaint& paint) {
+        SkASSERT(!fBlitter);
+        fBlitter = SkBlitter::Choose(device, matrix, paint,
+                                     fStorage, sizeof(fStorage));
+    }
+
+private:
+    SkBlitter*  fBlitter;
+    uint32_t    fStorage[kBlitterStorageLongCount];
+};
+
+SkAutoBlitterChoose::~SkAutoBlitterChoose() {
+    if ((void*)fBlitter == (void*)fStorage) {
+        fBlitter->~SkBlitter();
+    } else {
+        SkDELETE(fBlitter);
+    }
+}
+
+/**
+ *  Since we are providing the storage for the shader (to avoid the perf cost
+ *  of calling new) we insist that in our destructor we can account for all
+ *  owners of the shader.
+ */
+class SkAutoBitmapShaderInstall : SkNoncopyable {
+public:
+    SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint)
+            : fPaint(paint) /* makes a copy of the paint */ {
+        fPaint.setShader(SkShader::CreateBitmapShader(src,
+                           SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
+                           fStorage, sizeof(fStorage)));
+        // we deliberately left the shader with an owner-count of 2
+        SkASSERT(2 == fPaint.getShader()->getRefCnt());
+    }
+
+    ~SkAutoBitmapShaderInstall() {
+        SkShader* shader = fPaint.getShader();
+        // since we manually destroy shader, we insist that owners == 2
+        SkASSERT(2 == shader->getRefCnt());
+
+        fPaint.setShader(NULL); // unref the shader by 1
+
+        // now destroy to take care of the 2nd owner-count
+        if ((void*)shader == (void*)fStorage) {
+            shader->~SkShader();
+        } else {
+            SkDELETE(shader);
+        }
+    }
+
+    // return the new paint that has the shader applied
+    const SkPaint& paintWithShader() const { return fPaint; }
+
+private:
+    SkPaint     fPaint; // copy of caller's paint (which we then modify)
+    uint32_t    fStorage[kBlitterStorageLongCount];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDraw::SkDraw() {
+    sk_bzero(this, sizeof(*this));
+}
+
+SkDraw::SkDraw(const SkDraw& src) {
+    memcpy(this, &src, sizeof(*this));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
+
+static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
+    sk_bzero(pixels, bytes);
+}
+
+static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
+
+static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+    sk_memset32((uint32_t*)pixels, data, bytes >> 2);
+}
+
+static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+    sk_memset16((uint16_t*)pixels, data, bytes >> 1);
+}
+
+static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+    memset(pixels, data, bytes);
+}
+
+static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap,
+                                           const SkPaint& paint,
+                                           uint32_t* data) {
+    // todo: we can apply colorfilter up front if no shader, so we wouldn't
+    // need to abort this fastpath
+    if (paint.getShader() || paint.getColorFilter()) {
+        return NULL;
+    }
+
+    SkXfermode::Mode mode;
+    if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) {
+        return NULL;
+    }
+
+    SkColor color = paint.getColor();
+
+    // collaps modes based on color...
+    if (SkXfermode::kSrcOver_Mode == mode) {
+        unsigned alpha = SkColorGetA(color);
+        if (0 == alpha) {
+            mode = SkXfermode::kDst_Mode;
+        } else if (0xFF == alpha) {
+            mode = SkXfermode::kSrc_Mode;
+        }
+    }
+
+    switch (mode) {
+        case SkXfermode::kClear_Mode:
+//            SkDebugf("--- D_Clear_BitmapXferProc\n");
+            return D_Clear_BitmapXferProc;  // ignore data
+        case SkXfermode::kDst_Mode:
+//            SkDebugf("--- D_Dst_BitmapXferProc\n");
+            return D_Dst_BitmapXferProc;    // ignore data
+        case SkXfermode::kSrc_Mode: {
+            /*
+                should I worry about dithering for the lower depths?
+            */
+            SkPMColor pmc = SkPreMultiplyColor(color);
+            switch (bitmap.config()) {
+                case SkBitmap::kARGB_8888_Config:
+                    if (data) {
+                        *data = pmc;
+                    }
+//                    SkDebugf("--- D32_Src_BitmapXferProc\n");
+                    return D32_Src_BitmapXferProc;
+                case SkBitmap::kARGB_4444_Config:
+                    if (data) {
+                        *data = SkPixel32ToPixel4444(pmc);
+                    }
+//                    SkDebugf("--- D16_Src_BitmapXferProc\n");
+                    return D16_Src_BitmapXferProc;
+                case SkBitmap::kRGB_565_Config:
+                    if (data) {
+                        *data = SkPixel32ToPixel16(pmc);
+                    }
+//                    SkDebugf("--- D16_Src_BitmapXferProc\n");
+                    return D16_Src_BitmapXferProc;
+                case SkBitmap::kA8_Config:
+                    if (data) {
+                        *data = SkGetPackedA32(pmc);
+                    }
+//                    SkDebugf("--- DA8_Src_BitmapXferProc\n");
+                    return DA8_Src_BitmapXferProc;
+                default:
+                    break;
+            }
+            break;
+        }
+        default:
+            break;
+    }
+    return NULL;
+}
+
+static void CallBitmapXferProc(const SkBitmap& bitmap, const SkIRect& rect,
+                               BitmapXferProc proc, uint32_t procData) {
+    int shiftPerPixel;
+    switch (bitmap.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            shiftPerPixel = 2;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+        case SkBitmap::kRGB_565_Config:
+            shiftPerPixel = 1;
+            break;
+        case SkBitmap::kA8_Config:
+            shiftPerPixel = 0;
+            break;
+        default:
+            SkDEBUGFAIL("Can't use xferproc on this config");
+            return;
+    }
+
+    uint8_t* pixels = (uint8_t*)bitmap.getPixels();
+    SkASSERT(pixels);
+    const size_t rowBytes = bitmap.rowBytes();
+    const int widthBytes = rect.width() << shiftPerPixel;
+
+    // skip down to the first scanline and X position
+    pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
+    for (int scans = rect.height() - 1; scans >= 0; --scans) {
+        proc(pixels, widthBytes, procData);
+        pixels += rowBytes;
+    }
+}
+
+void SkDraw::drawPaint(const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (fRC->isEmpty()) {
+        return;
+    }
+
+    SkIRect    devRect;
+    devRect.set(0, 0, fBitmap->width(), fBitmap->height());
+    if (fBounder && !fBounder->doIRect(devRect)) {
+        return;
+    }
+
+    if (fRC->isBW()) {
+        /*  If we don't have a shader (i.e. we're just a solid color) we may
+            be faster to operate directly on the device bitmap, rather than invoking
+            a blitter. Esp. true for xfermodes, which require a colorshader to be
+            present, which is just redundant work. Since we're drawing everywhere
+            in the clip, we don't have to worry about antialiasing.
+        */
+        uint32_t procData = 0;  // to avoid the warning
+        BitmapXferProc proc = ChooseBitmapXferProc(*fBitmap, paint, &procData);
+        if (proc) {
+            if (D_Dst_BitmapXferProc == proc) { // nothing to do
+                return;
+            }
+
+            SkRegion::Iterator iter(fRC->bwRgn());
+            while (!iter.done()) {
+                CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData);
+                iter.next();
+            }
+            return;
+        }
+    }
+
+    // normal case: use a blitter
+    SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+    SkScan::FillIRect(devRect, *fRC, blitter.get());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct PtProcRec {
+    SkCanvas::PointMode fMode;
+    const SkPaint*  fPaint;
+    const SkRegion* fClip;
+    const SkRasterClip* fRC;
+
+    // computed values
+    SkFixed fRadius;
+
+    typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
+                         SkBlitter*);
+
+    bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
+              const SkRasterClip*);
+    Proc chooseProc(SkBlitter** blitter);
+
+private:
+    SkAAClipBlitterWrapper fWrapper;
+};
+
+static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                                 int count, SkBlitter* blitter) {
+    SkASSERT(rec.fClip->isRect());
+    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);
+        if (r.contains(x, y)) {
+            blitter->blitH(x, y, 1);
+        }
+    }
+}
+
+static void bw_pt_rect_16_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);
+
+    uint16_t* addr = bitmap->getAddr16(0, 0);
+    int rb = bitmap->rowBytes();
+
+    for (int i = 0; i < count; i++) {
+        int x = SkScalarFloor(devPts[i].fX);
+        int y = SkScalarFloor(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_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                            int count, SkBlitter* blitter) {
+    for (int i = 0; i < count; i++) {
+        int x = SkScalarFloor(devPts[i].fX);
+        int y = SkScalarFloor(devPts[i].fY);
+        if (rec.fClip->contains(x, y)) {
+            blitter->blitH(x, y, 1);
+        }
+    }
+}
+
+static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count; i += 2) {
+        SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
+    }
+}
+
+static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count - 1; i++) {
+        SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
+    }
+}
+
+// aa versions
+
+static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count; i += 2) {
+        SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
+    }
+}
+
+static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count - 1; i++) {
+        SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
+    }
+}
+
+// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
+
+static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
+                           int count, SkBlitter* blitter) {
+    const SkFixed radius = rec.fRadius;
+    for (int i = 0; i < count; i++) {
+        SkFixed x = SkScalarToFixed(devPts[i].fX);
+        SkFixed y = SkScalarToFixed(devPts[i].fY);
+
+        SkXRect r;
+        r.fLeft = x - radius;
+        r.fTop = y - radius;
+        r.fRight = x + radius;
+        r.fBottom = y + radius;
+
+        SkScan::FillXRect(r, *rec.fRC, blitter);
+    }
+}
+
+static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
+                           int count, SkBlitter* blitter) {
+    const SkFixed radius = rec.fRadius;
+    for (int i = 0; i < count; i++) {
+        SkFixed x = SkScalarToFixed(devPts[i].fX);
+        SkFixed y = SkScalarToFixed(devPts[i].fY);
+
+        SkXRect r;
+        r.fLeft = x - radius;
+        r.fTop = y - radius;
+        r.fRight = x + radius;
+        r.fBottom = y + radius;
+
+        SkScan::AntiFillXRect(r, *rec.fRC, blitter);
+    }
+}
+
+// If this guy returns true, then chooseProc() must return a valid proc
+bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
+                     const SkMatrix* matrix, const SkRasterClip* rc) {
+    if (paint.getPathEffect()) {
+        return false;
+    }
+    SkScalar width = paint.getStrokeWidth();
+    if (0 == width) {
+        fMode = mode;
+        fPaint = &paint;
+        fClip = NULL;
+        fRC = rc;
+        fRadius = SK_Fixed1 >> 1;
+        return true;
+    }
+    if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
+            matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) {
+        SkScalar sx = matrix->get(SkMatrix::kMScaleX);
+        SkScalar sy = matrix->get(SkMatrix::kMScaleY);
+        if (SkScalarNearlyZero(sx - sy)) {
+            if (sx < 0) {
+                sx = -sx;
+            }
+
+            fMode = mode;
+            fPaint = &paint;
+            fClip = NULL;
+            fRC = rc;
+            fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
+            return true;
+        }
+    }
+    return false;
+}
+
+PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
+    Proc proc = NULL;
+
+    SkBlitter* blitter = *blitterPtr;
+    if (fRC->isBW()) {
+        fClip = &fRC->bwRgn();
+    } else {
+        fWrapper.init(*fRC, blitter);
+        fClip = &fWrapper.getRgn();
+        blitter = fWrapper.getBlitter();
+        *blitterPtr = blitter;
+    }
+
+    // for our arrays
+    SkASSERT(0 == SkCanvas::kPoints_PointMode);
+    SkASSERT(1 == SkCanvas::kLines_PointMode);
+    SkASSERT(2 == SkCanvas::kPolygon_PointMode);
+    SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
+
+    // first check for hairlines
+    if (0 == fPaint->getStrokeWidth()) {
+        if (fPaint->isAntiAlias()) {
+            static const Proc gAAProcs[] = {
+                aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
+            };
+            proc = gAAProcs[fMode];
+        } else {
+            if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
+                uint32_t value;
+                const SkBitmap* bm = blitter->justAnOpaqueColor(&value);
+                if (bm && bm->config() == SkBitmap::kRGB_565_Config) {
+                    proc = bw_pt_rect_16_hair_proc;
+                } else {
+                    proc = bw_pt_rect_hair_proc;
+                }
+            } else {
+                static Proc gBWProcs[] = {
+                    bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
+                };
+                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;
+        }
+    }
+    return proc;
+}
+
+static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode,
+                           size_t count, const SkPoint pts[],
+                           const SkPaint& paint, const SkMatrix& matrix) {
+    SkIRect ibounds;
+    SkRect bounds;
+    SkScalar inset = paint.getStrokeWidth();
+
+    bounds.set(pts, count);
+    bounds.inset(-inset, -inset);
+    matrix.mapRect(&bounds);
+
+    bounds.roundOut(&ibounds);
+    return bounder->doIRect(ibounds);
+}
+
+// each of these costs 8-bytes of stack space, so don't make it too large
+// must be even for lines/polygon to work
+#define MAX_DEV_PTS     32
+
+void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
+                        const SkPoint pts[], const SkPaint& paint,
+                        bool forceUseDevice) const {
+    // if we're in lines mode, force count to be even
+    if (SkCanvas::kLines_PointMode == mode) {
+        count &= ~(size_t)1;
+    }
+
+    if ((long)count <= 0) {
+        return;
+    }
+
+    SkASSERT(pts != NULL);
+    SkDEBUGCODE(this->validate();)
+
+     // nothing to draw
+    if (fRC->isEmpty()) {
+        return;
+    }
+
+    if (fBounder) {
+        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);
+        noBounder.fBounder = NULL;
+        noBounder.drawPoints(mode, count, pts, paint, forceUseDevice);
+        return;
+    }
+    
+    PtProcRec rec;
+    if (!forceUseDevice && rec.init(mode, paint, fMatrix, fRC)) {
+        SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+        SkPoint             devPts[MAX_DEV_PTS];
+        const SkMatrix*     matrix = fMatrix;
+        SkBlitter*          bltr = blitter.get();
+        PtProcRec::Proc     proc = rec.chooseProc(&bltr);
+        // we have to back up subsequent passes if we're in polygon mode
+        const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
+
+        do {
+            size_t n = count;
+            if (n > MAX_DEV_PTS) {
+                n = MAX_DEV_PTS;
+            }
+            matrix->mapPoints(devPts, pts, n);
+            proc(rec, devPts, n, bltr);
+            pts += n - backup;
+            SkASSERT(count >= n);
+            count -= n;
+            if (count > 0) {
+                count += backup;
+            }
+        } while (count != 0);
+    } else {
+        switch (mode) {
+            case SkCanvas::kPoints_PointMode: {
+                // temporarily mark the paint as filling.
+                SkPaint newPaint(paint);
+                newPaint.setStyle(SkPaint::kFill_Style);
+
+                SkScalar width = newPaint.getStrokeWidth();
+                SkScalar radius = SkScalarHalf(width);
+
+                if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
+                    SkPath      path;
+                    SkMatrix    preMatrix;
+
+                    path.addCircle(0, 0, radius);
+                    for (size_t i = 0; i < count; i++) {
+                        preMatrix.setTranslate(pts[i].fX, pts[i].fY);
+                        // pass true for the last point, since we can modify
+                        // then path then
+                        if (fDevice) {
+                            fDevice->drawPath(*this, path, newPaint, &preMatrix,
+                                              (count-1) == i);
+                        } else {
+                            this->drawPath(path, newPaint, &preMatrix,
+                                           (count-1) == i);
+                        }
+                    }
+                } else {
+                    SkRect  r;
+
+                    for (size_t i = 0; i < count; i++) {
+                        r.fLeft = pts[i].fX - radius;
+                        r.fTop = pts[i].fY - radius;
+                        r.fRight = r.fLeft + width;
+                        r.fBottom = r.fTop + width;
+                        if (fDevice) {
+                            fDevice->drawRect(*this, r, newPaint);
+                        } else {
+                            this->drawRect(r, newPaint);
+                        }
+                    }
+                }
+                break;
+            }
+            case SkCanvas::kLines_PointMode:
+            case SkCanvas::kPolygon_PointMode: {
+                count -= 1;
+                SkPath path;
+                SkPaint p(paint);
+                p.setStyle(SkPaint::kStroke_Style);
+                size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
+                for (size_t i = 0; i < count; i += inc) {
+                    path.moveTo(pts[i]);
+                    path.lineTo(pts[i+1]);
+                    if (fDevice) {
+                        fDevice->drawPath(*this, path, p, NULL, true);
+                    } else {
+                        this->drawPath(path, p, NULL, true);
+                    }
+                    path.rewind();
+                }
+                break;
+            }
+        }
+    }
+}
+
+static inline SkPoint* as_lefttop(SkRect* r) {
+    return (SkPoint*)(void*)r;
+}
+
+static inline SkPoint* as_rightbottom(SkRect* r) {
+    return ((SkPoint*)(void*)r) + 1;
+}
+
+static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
+                           SkPoint* strokeSize) {
+    if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
+        paint.getStrokeMiter() < SK_ScalarSqrt2) {
+        return false;
+    }
+    
+    SkASSERT(matrix.rectStaysRect());
+    SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
+    matrix.mapVectors(strokeSize, &pt, 1);
+    strokeSize->fX = SkScalarAbs(strokeSize->fX);
+    strokeSize->fY = SkScalarAbs(strokeSize->fY);
+    return true;
+}
+
+SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
+                                         const SkMatrix& matrix,
+                                         SkPoint* strokeSize) {
+    RectType rtype;
+    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) {
+        rtype = kPath_RectType;
+    } else if (SkPaint::kFill_Style == style) {
+        rtype = kFill_RectType;
+    } else if (zeroWidth) {
+        rtype = kHair_RectType;
+    } else if (easy_rect_join(paint, matrix, strokeSize)) {
+        rtype = kStroke_RectType;
+    } else {
+        rtype = kPath_RectType;
+    }
+    return rtype;
+}
+
+static SkPoint* rect_points(SkRect& r, int index) {
+    SkASSERT((unsigned)index < 2);
+    return &((SkPoint*)(void*)&r)[index];
+}
+
+void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (fRC->isEmpty()) {
+        return;
+    }
+
+    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);
+        tmp.setFillType(SkPath::kWinding_FillType);
+        this->drawPath(tmp, paint, NULL, true);
+        return;
+    }
+
+    const SkMatrix& matrix = *fMatrix;
+    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();
+    }
+
+    if (fBounder && !fBounder->doRect(devRect, paint)) {
+        return;
+    }
+
+    // look for the quick exit, before we build a blitter
+    {
+        SkIRect ir;
+        devRect.roundOut(&ir);
+        if (paint.getStyle() != SkPaint::kFill_Style) {
+            // extra space for hairlines
+            ir.inset(-1, -1);
+        }
+        if (fRC->quickReject(ir))
+            return;
+    }
+
+    SkAutoBlitterChoose blitterStorage(*fBitmap, matrix, paint);
+    const SkRasterClip& clip = *fRC;
+    SkBlitter*          blitter = blitterStorage.get();
+
+    // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
+    // case we are also hairline (if we've gotten to here), which devolves to
+    // effectively just kFill
+    switch (rtype) {
+        case kFill_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFillRect(devRect, clip, blitter);
+            } else {
+                SkScan::FillRect(devRect, clip, blitter);
+            }
+            break;
+        case kStroke_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
+            } else {
+                SkScan::FrameRect(devRect, strokeSize, clip, blitter);
+            }
+            break;
+        case kHair_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiHairRect(devRect, clip, blitter);
+            } else {
+                SkScan::HairRect(devRect, clip, blitter);
+            }
+            break;
+        default:
+            SkDEBUGFAIL("bad rtype");
+    }
+}
+
+void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
+    if (srcM.fBounds.isEmpty()) {
+        return;
+    }
+
+    const SkMask* mask = &srcM;
+
+    SkMask dstM;
+    if (paint.getMaskFilter() &&
+            paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) {
+        mask = &dstM;
+    } else {
+        dstM.fImage = NULL;
+    }
+    SkAutoMaskFreeImage ami(dstM.fImage);
+
+    if (fBounder && !fBounder->doIRect(mask->fBounds)) {
+        return;
+    }
+
+    SkAutoBlitterChoose blitterChooser(*fBitmap, *fMatrix, paint);
+    SkBlitter* blitter = blitterChooser.get();
+
+    SkAAClipBlitterWrapper wrapper;
+    const SkRegion* clipRgn;
+
+    if (fRC->isBW()) {
+        clipRgn = &fRC->bwRgn();
+    } else {
+        wrapper.init(*fRC, blitter);
+        clipRgn = &wrapper.getRgn();
+        blitter = wrapper.getBlitter();
+    }
+    blitter->blitMaskRegion(*mask, *clipRgn);
+}
+
+static SkScalar fast_len(const SkVector& vec) {
+    SkScalar x = SkScalarAbs(vec.fX);
+    SkScalar y = SkScalarAbs(vec.fY);
+    if (x < y) {
+        SkTSwap(x, y);
+    }
+    return x + SkScalarHalf(y);
+}
+
+static bool xfermodeSupportsCoverageAsAlpha(SkXfermode* xfer) {
+    SkXfermode::Coeff dc;
+    if (!SkXfermode::AsCoeff(xfer, NULL, &dc)) {
+        return false;
+    }
+    
+    switch (dc) {
+        case SkXfermode::kOne_Coeff:
+        case SkXfermode::kISA_Coeff:
+        case SkXfermode::kISC_Coeff:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
+                           SkScalar* coverage) {
+    SkASSERT(coverage);
+    if (SkPaint::kStroke_Style != paint.getStyle()) {
+        return false;
+    }
+    SkScalar strokeWidth = paint.getStrokeWidth();
+    if (0 == strokeWidth) {
+        *coverage = SK_Scalar1;
+        return true;
+    }
+
+    // if we get here, we need to try to fake a thick-stroke with a modulated
+    // hairline
+
+    if (!paint.isAntiAlias()) {
+        return false;
+    }
+    if (matrix.hasPerspective()) {
+        return false;
+    }
+
+    SkVector src[2], dst[2];
+    src[0].set(strokeWidth, 0);
+    src[1].set(0, strokeWidth);
+    matrix.mapVectors(dst, src, 2);
+    SkScalar len0 = fast_len(dst[0]);
+    SkScalar len1 = fast_len(dst[1]);
+    if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
+        *coverage = SkScalarAve(len0, len1);
+        return true;
+    }
+    return false;
+}
+
+void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
+                      const SkMatrix* prePathMatrix, bool pathIsMutable) const {
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (fRC->isEmpty()) {
+        return;
+    }
+
+    SkPath*         pathPtr = (SkPath*)&origSrcPath;
+    bool            doFill = true;
+    SkPath          tmpPath;
+    SkMatrix        tmpMatrix;
+    const SkMatrix* matrix = fMatrix;
+
+    if (prePathMatrix) {
+        if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style ||
+                origPaint.getRasterizer()) {
+            SkPath* result = pathPtr;
+
+            if (!pathIsMutable) {
+                result = &tmpPath;
+                pathIsMutable = true;
+            }
+            pathPtr->transform(*prePathMatrix, result);
+            pathPtr = result;
+        } else {
+            if (!tmpMatrix.setConcat(*matrix, *prePathMatrix)) {
+                // overflow
+                return;
+            }
+            matrix = &tmpMatrix;
+        }
+    }
+    // at this point we're done with prePathMatrix
+    SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
+
+    const SkPaint* paint = &origPaint;
+    SkTLazy<SkPaint> lazyPaint;
+
+    {
+        SkScalar coverage;
+        if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
+            if (SK_Scalar1 == coverage) {
+                lazyPaint.set(origPaint);
+                lazyPaint.get()->setStrokeWidth(0);
+                paint = lazyPaint.get();
+            } else if (xfermodeSupportsCoverageAsAlpha(origPaint.getXfermode())) {
+                U8CPU newAlpha;
+#if 0
+                newAlpha = SkToU8(SkScalarRoundToInt(coverage *
+                                                     origPaint.getAlpha()));
+#else
+                // this is the old technique, which we preserve for now so
+                // we don't change previous results (testing)
+                // the new way seems fine, its just (a tiny bit) different
+                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();
+            }
+        }
+    }
+
+    if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
+        doFill = paint->getFillPath(*pathPtr, &tmpPath);
+        pathPtr = &tmpPath;
+    }
+
+    if (paint->getRasterizer()) {
+        SkMask  mask;
+        if (paint->getRasterizer()->rasterize(*pathPtr, *matrix,
+                            &fRC->getBounds(), paint->getMaskFilter(), &mask,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
+            this->drawDevMask(mask, *paint);
+            SkMask::FreeImage(mask.fImage);
+        }
+        return;
+    }
+
+    // avoid possibly allocating a new path in transform if we can
+    SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
+
+    // transform the path into device space
+    pathPtr->transform(*matrix, devPathPtr);
+
+    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 (fBounder && !fBounder->doPath(*devPathPtr, *paint, doFill)) {
+        return;
+    }
+
+    void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
+    if (doFill) {
+        if (paint->isAntiAlias()) {
+            proc = SkScan::AntiFillPath;
+        } else {
+            proc = SkScan::FillPath;
+        }
+    } else {    // hairline
+        if (paint->isAntiAlias()) {
+            proc = SkScan::AntiHairPath;
+        } else {
+            proc = SkScan::HairPath;
+        }
+    }
+    proc(*devPathPtr, *fRC, blitter.get());
+}
+
+/** 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_translate(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;
+}
+
+void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
+                              const SkPaint& paint) const {
+    SkASSERT(bitmap.getConfig() == SkBitmap::kA8_Config);
+
+    if (just_translate(*fMatrix, bitmap)) {
+        int ix = SkScalarRound(fMatrix->getTranslateX());
+        int iy = SkScalarRound(fMatrix->getTranslateY());
+
+        SkMask  mask;
+        mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
+        mask.fFormat = SkMask::kA8_Format;
+        mask.fRowBytes = bitmap.rowBytes();
+        mask.fImage = bitmap.getAddr8(0, 0);
+
+        this->drawDevMask(mask, paint);
+    } else {    // need to xform the bitmap first
+        SkRect  r;
+        SkMask  mask;
+
+        r.set(0, 0,
+              SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
+        fMatrix->mapRect(&r);
+        r.round(&mask.fBounds);
+
+        // set the mask's bounds to the transformed bitmap-bounds,
+        // clipped to the actual device
+        {
+            SkIRect    devBounds;
+            devBounds.set(0, 0, fBitmap->width(), fBitmap->height());
+            // need intersect(l, t, r, b) on irect
+            if (!mask.fBounds.intersect(devBounds)) {
+                return;
+            }
+        }
+
+        mask.fFormat = SkMask::kA8_Format;
+        mask.fRowBytes = SkAlign4(mask.fBounds.width());
+        size_t size = mask.computeImageSize();
+        if (0 == size) {
+            // the mask is too big to allocated, draw nothing
+            return;
+        }
+
+        // allocate (and clear) our temp buffer to hold the transformed bitmap
+        SkAutoMalloc    storage(size);
+        mask.fImage = (uint8_t*)storage.get();
+        memset(mask.fImage, 0, size);
+
+        // now draw our bitmap(src) into mask(dst), transformed by the matrix
+        {
+            SkBitmap    device;
+            device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
+                             mask.fBounds.height(), mask.fRowBytes);
+            device.setPixels(mask.fImage);
+
+            SkCanvas c(device);
+            // need the unclipped top/left for the translate
+            c.translate(-SkIntToScalar(mask.fBounds.fLeft),
+                        -SkIntToScalar(mask.fBounds.fTop));
+            c.concat(*fMatrix);
+
+            // We can't call drawBitmap, or we'll infinitely recurse. Instead
+            // we manually build a shader and draw that into our new mask
+            SkPaint tmpPaint;
+            tmpPaint.setFlags(paint.getFlags());
+            SkAutoBitmapShaderInstall install(bitmap, tmpPaint);
+            SkRect rr;
+            rr.set(0, 0, SkIntToScalar(bitmap.width()),
+                   SkIntToScalar(bitmap.height()));
+            c.drawRect(rr, install.paintWithShader());
+        }
+        this->drawDevMask(mask, paint);
+    }
+}
+
+static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
+                        const SkRect& srcR) {
+    SkRect  dstR;
+    SkIRect devIR;
+
+    m.mapRect(&dstR, srcR);
+    dstR.roundOut(&devIR);
+    return c.quickReject(devIR);
+}
+
+static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
+                        int width, int height) {
+    SkRect  r;
+    r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
+    return clipped_out(matrix, clip, r);
+}
+
+static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y,
+                              const SkBitmap& bitmap) {
+    return clip.isBW() ||
+           clip.quickContains(x, y, x + bitmap.width(), y + bitmap.height());
+}
+
+void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
+                        const SkPaint& origPaint) const {
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (fRC->isEmpty() ||
+            bitmap.width() == 0 || bitmap.height() == 0 ||
+            bitmap.getConfig() == SkBitmap::kNo_Config) {
+        return;
+    }
+
+#ifndef SK_ALLOW_OVER_32K_BITMAPS
+    // run away on too-big bitmaps for now (exceed 16.16)
+    if (bitmap.width() > 32767 || bitmap.height() > 32767) {
+        return;
+    }
+#endif
+
+    SkPaint paint(origPaint);
+    paint.setStyle(SkPaint::kFill_Style);
+
+    SkMatrix matrix;
+    if (!matrix.setConcat(*fMatrix, prematrix)) {
+        return;
+    }
+
+    if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
+        return;
+    }
+
+    if (fBounder && just_translate(matrix, bitmap)) {
+        SkIRect ir;
+        int32_t ix = SkScalarRound(matrix.getTranslateX());
+        int32_t iy = SkScalarRound(matrix.getTranslateY());
+        ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
+        if (!fBounder->doIRect(ir)) {
+            return;
+        }
+    }
+
+    // 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)) {
+        int ix = SkScalarRound(matrix.getTranslateX());
+        int iy = SkScalarRound(matrix.getTranslateY());
+        if (clipHandlesSprite(*fRC, ix, iy, bitmap)) {
+            uint32_t    storage[kBlitterStorageLongCount];
+            SkBlitter*  blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+                                                ix, iy, storage, sizeof(storage));
+            if (blitter) {
+                SkAutoTPlacementDelete<SkBlitter>   ad(blitter, storage);
+
+                SkIRect    ir;
+                ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
+
+                SkScan::FillIRect(ir, *fRC, blitter);
+                return;
+            }
+        }
+    }
+
+    // now make a temp draw on the stack, and use it
+    //
+    SkDraw draw(*this);
+    draw.fMatrix = &matrix;
+
+    if (bitmap.getConfig() == SkBitmap::kA8_Config) {
+        draw.drawBitmapAsMask(bitmap, paint);
+    } else {
+        SkAutoBitmapShaderInstall install(bitmap, paint);
+
+        SkRect  r;
+        r.set(0, 0, SkIntToScalar(bitmap.width()),
+              SkIntToScalar(bitmap.height()));
+        // is this ok if paint has a rasterizer?
+        draw.drawRect(r, install.paintWithShader());
+    }
+}
+
+void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y,
+                        const SkPaint& origPaint) const {
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (fRC->isEmpty() ||
+            bitmap.width() == 0 || bitmap.height() == 0 ||
+            bitmap.getConfig() == SkBitmap::kNo_Config) {
+        return;
+    }
+
+    SkIRect    bounds;
+    bounds.set(x, y, x + bitmap.width(), y + bitmap.height());
+
+    if (fRC->quickReject(bounds)) {
+        return; // nothing to draw
+    }
+
+    SkPaint paint(origPaint);
+    paint.setStyle(SkPaint::kFill_Style);
+
+    if (NULL == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, bitmap)) {
+        uint32_t    storage[kBlitterStorageLongCount];
+        SkBlitter*  blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+                                                x, y, storage, sizeof(storage));
+
+        if (blitter) {
+            SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
+
+            if (fBounder && !fBounder->doIRect(bounds)) {
+                return;
+            }
+
+            SkScan::FillIRect(bounds, *fRC, blitter);
+            return;
+        }
+    }
+
+    SkAutoBitmapShaderInstall install(bitmap, paint);
+    const SkPaint& shaderPaint = install.paintWithShader();
+
+    SkMatrix        matrix;
+    SkRect          r;
+
+    // get a scalar version of our rect
+    r.set(bounds);
+
+    // tell the shader our offset
+    matrix.setTranslate(r.fLeft, r.fTop);
+    shaderPaint.getShader()->setLocalMatrix(matrix);
+
+    SkDraw draw(*this);
+    matrix.reset();
+    draw.fMatrix = &matrix;
+    // call ourself with a rect
+    // is this OK if paint has a rasterizer?
+    draw.drawRect(r, shaderPaint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkScalerContext.h"
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+
+static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
+                const char text[], size_t byteLength, SkVector* stopVector) {
+    SkFixed     x = 0, y = 0;
+    const char* stop = text + byteLength;
+
+    SkAutoKern  autokern;
+
+    while (text < stop) {
+        // don't need x, y here, since all subpixel variants will have the
+        // same advance
+        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+        x += autokern.adjust(glyph) + glyph.fAdvanceX;
+        y += glyph.fAdvanceY;
+    }
+    stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
+
+    SkASSERT(text == stop);
+}
+
+void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
+                              SkScalar x, SkScalar y,
+                              const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkTextToPathIter iter(text, byteLength, paint, true, true);
+
+    SkMatrix    matrix;
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+
+    const SkPath* iterPath;
+    SkScalar xpos, prevXPos = 0;
+
+    while ((iterPath = iter.next(&xpos)) != NULL) {
+        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);
+        }
+        prevXPos = xpos;
+    }
+}
+
+// disable warning : local variable used without having been initialized
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state,
+                                   SkFixed fx, SkFixed fy,
+                                   const SkGlyph& glyph) {
+    int left = SkFixedFloor(fx);
+    int top = SkFixedFloor(fy);
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+    SkASSERT(NULL == state.fBounder);
+    SkASSERT((NULL == state.fClip && state.fAAClip) ||
+             (state.fClip && NULL == state.fAAClip && state.fClip->isRect()));
+
+    left += glyph.fLeft;
+    top  += glyph.fTop;
+
+    int right   = left + glyph.fWidth;
+    int bottom  = top + glyph.fHeight;
+
+    SkMask		mask;
+    SkIRect		storage;
+    SkIRect*	bounds = &mask.fBounds;
+
+    mask.fBounds.set(left, top, right, bottom);
+
+    // this extra test is worth it, assuming that most of the time it succeeds
+    // since we can avoid writing to storage
+    if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) {
+        if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds))
+            return;
+        bounds = &storage;
+    }
+
+    uint8_t* aa = (uint8_t*)glyph.fImage;
+    if (NULL == aa) {
+        aa = (uint8_t*)state.fCache->findImage(glyph);
+        if (NULL == aa) {
+            return; // can't rasterize glyph
+        }
+    }
+
+    mask.fRowBytes = glyph.rowBytes();
+    mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
+    mask.fImage = aa;
+    state.fBlitter->blitMask(mask, *bounds);
+}
+
+static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
+                                  SkFixed fx, SkFixed fy,
+                                  const SkGlyph& glyph) {
+    int left = SkFixedFloor(fx);
+    int top = SkFixedFloor(fy);
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+    SkASSERT(!state.fClip->isRect());
+    SkASSERT(NULL == state.fBounder);
+
+    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;
+        if (NULL == aa) {
+            aa = (uint8_t*)state.fCache->findImage(glyph);
+            if (NULL == aa) {
+            	return;
+            }
+        }
+
+        mask.fRowBytes = glyph.rowBytes();
+        mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
+        mask.fImage = (uint8_t*)aa;
+        do {
+            state.fBlitter->blitMask(mask, cr);
+            clipper.next();
+        } while (!clipper.done());
+    }
+}
+
+static void D1G_Bounder(const SkDraw1Glyph& state,
+                        SkFixed fx, SkFixed fy,
+                        const SkGlyph& glyph) {
+    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;
+        if (NULL == aa) {
+            aa = (uint8_t*)state.fCache->findImage(glyph);
+            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,
+                                         left - glyph.fLeft,
+                                         top - glyph.fTop, glyph)) {
+            mask.fRowBytes = glyph.rowBytes();
+            mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
+            mask.fImage = (uint8_t*)aa;
+            do {
+                state.fBlitter->blitMask(mask, cr);
+                clipper.next();
+            } while (!clipper.done());
+        }
+    }
+}
+
+static void D1G_Bounder_AAClip(const SkDraw1Glyph& state,
+                               SkFixed fx, SkFixed fy,
+                               const SkGlyph& glyph) {
+    int left = SkFixedFloor(fx);
+    int top = SkFixedFloor(fy);
+    SkIRect bounds;
+    bounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
+
+    if (state.fBounder->doIRectGlyph(bounds, left, top, glyph)) {
+        D1G_NoBounder_RectClip(state, fx, fy, glyph);
+    }
+}
+
+static bool hasCustomD1GProc(const SkDraw& draw) {
+    return draw.fProcs && draw.fProcs->fD1GProc;
+}
+
+static bool needsRasterTextBlit(const SkDraw& draw) {
+    return !hasCustomD1GProc(draw);
+}
+
+SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
+                                      SkGlyphCache* cache) {
+    fDraw = draw;
+    fBounder = draw->fBounder;
+    fBlitter = blitter;
+    fCache = cache;
+
+    if (hasCustomD1GProc(*draw)) {
+        // todo: fix this assumption about clips w/ custom
+        fClip = draw->fClip;
+        fClipBounds = fClip->getBounds();
+        return draw->fProcs->fD1GProc;
+    }
+
+    if (draw->fRC->isBW()) {
+        fAAClip = NULL;
+        fClip = &draw->fRC->bwRgn();
+        fClipBounds = fClip->getBounds();
+        if (NULL == fBounder) {
+            if (fClip->isRect()) {
+                return D1G_NoBounder_RectClip;
+            } else {
+                return D1G_NoBounder_RgnClip;
+            }
+        } else {
+            return D1G_Bounder;
+        }
+    } else {    // aaclip
+        fAAClip = &draw->fRC->aaRgn();
+        fClip = NULL;
+        fClipBounds = fAAClip->getBounds();
+        if (NULL == fBounder) {
+            return D1G_NoBounder_RectClip;
+        } else {
+            return D1G_Bounder_AAClip;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDraw::drawText(const char text[], size_t byteLength,
+                      SkScalar x, SkScalar y, const SkPaint& paint) const {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
+        return;
+    }
+
+    if (/*paint.isLinearText() ||*/
+        (fMatrix->hasPerspective())) {
+        this->drawText_asPaths(text, byteLength, x, y, paint);
+        return;
+    }
+
+    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);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    // transform our starting point
+    {
+        SkPoint loc;
+        matrix->mapXY(x, y, &loc);
+        x = loc.fX;
+        y = loc.fY;
+    }
+
+    // need to measure first
+    if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkVector    stop;
+
+        measure_text(cache, glyphCacheProc, text, byteLength, &stop);
+
+        SkScalar    stopX = stop.fX;
+        SkScalar    stopY = stop.fY;
+
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            stopX = SkScalarHalf(stopX);
+            stopY = SkScalarHalf(stopY);
+        }
+        x -= stopX;
+        y -= stopY;
+    }
+
+    SkFixed fx = SkScalarToFixed(x);
+    SkFixed fy = SkScalarToFixed(y);
+    const char* stop = text + byteLength;
+
+    SkFixed fxMask = ~0;
+    SkFixed fyMask = ~0;
+    if (cache->isSubpixel()) {
+        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*matrix);
+        if (kX_SkAxisAlignment == baseline) {
+            fyMask = 0;
+        } 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;
+    } else {
+        fx += SK_FixedHalf;
+        fy += SK_FixedHalf;
+    }
+
+    SkAAClipBlitter     aaBlitter;
+    SkAutoBlitterChoose blitterChooser;
+    SkBlitter*          blitter = NULL;
+    if (needsRasterTextBlit(*this)) {
+        blitterChooser.choose(*fBitmap, *matrix, paint);
+        blitter = blitterChooser.get();
+        if (fRC->isAA()) {
+            aaBlitter.init(blitter, &fRC->aaRgn());
+            blitter = &aaBlitter;
+        }
+    }
+
+    SkAutoKern          autokern;
+    SkDraw1Glyph        d1g;
+    SkDraw1Glyph::Proc  proc = d1g.init(this, blitter, cache);
+
+    while (text < stop) {
+        const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
+
+        fx += autokern.adjust(glyph);
+
+        if (glyph.fWidth) {
+            proc(d1g, fx, fy, glyph);
+        }
+        fx += glyph.fAdvanceX;
+        fy += glyph.fAdvanceY;
+    }
+}
+
+// last parameter is interpreted as SkFixed [x, y]
+// return the fixed position, which may be rounded or not by the caller
+//   e.g. subpixel doesn't round
+typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
+
+static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+                          SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
+}
+
+static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+                            SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
+             SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
+}
+
+static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+                           SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
+             SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
+}
+
+static AlignProc pick_align_proc(SkPaint::Align align) {
+    static const AlignProc gProcs[] = {
+        leftAlignProc, centerAlignProc, rightAlignProc
+    };
+
+    SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
+
+    return gProcs[align];
+}
+
+class TextMapState {
+public:
+    mutable SkPoint fLoc;
+
+    TextMapState(const SkMatrix& matrix, SkScalar y)
+        : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
+
+    typedef void (*Proc)(const TextMapState&, const SkScalar pos[]);
+
+    Proc pickProc(int scalarsPerPosition);
+
+private:
+    const SkMatrix&     fMatrix;
+    SkMatrix::MapXYProc fProc;
+    SkScalar            fY; // ignored by MapXYProc
+    // these are only used by Only... procs
+    SkScalar            fScaleX, fTransX, fTransformedY;
+
+    static void MapXProc(const TextMapState& state, const SkScalar pos[]) {
+        state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
+    }
+
+    static void MapXYProc(const TextMapState& state, const SkScalar pos[]) {
+        state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
+    }
+
+    static void MapOnlyScaleXProc(const TextMapState& state,
+                                  const SkScalar pos[]) {
+        state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
+                       state.fTransformedY);
+    }
+
+    static void MapOnlyTransXProc(const TextMapState& state,
+                                  const SkScalar pos[]) {
+        state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
+    }
+};
+
+TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) {
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    if (1 == scalarsPerPosition) {
+        unsigned mtype = fMatrix.getType();
+        if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
+            return MapXProc;
+        } else {
+            fScaleX = fMatrix.getScaleX();
+            fTransX = fMatrix.getTranslateX();
+            fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
+                            fMatrix.getTranslateY();
+            return (mtype & SkMatrix::kScale_Mask) ?
+                        MapOnlyScaleXProc : MapOnlyTransXProc;
+        }
+    } else {
+        return MapXYProc;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void SkDraw::drawPosText(const char text[], size_t byteLength,
+                         const SkScalar pos[], SkScalar constY,
+                         int scalarsPerPosition, const SkPaint& paint) const {
+    SkASSERT(byteLength == 0 || text != NULL);
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
+        return;
+    }
+
+    if (/*paint.isLinearText() ||*/
+        (fMatrix->hasPerspective())) {
+        // TODO !!!!
+//      this->drawText_asPaths(text, byteLength, x, y, paint);
+        return;
+    }
+
+    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);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    SkAAClipBlitterWrapper wrapper;
+    SkAutoBlitterChoose blitterChooser;
+    SkBlitter* blitter = NULL;
+    if (needsRasterTextBlit(*this)) {
+        blitterChooser.choose(*fBitmap, *matrix, paint);
+        blitter = blitterChooser.get();
+        if (fRC->isAA()) {
+            wrapper.init(*fRC, blitter);
+            blitter = wrapper.getBlitter();
+        }
+    }
+    
+    const char*        stop = text + byteLength;
+    AlignProc          alignProc = pick_align_proc(paint.getTextAlign());
+    SkDraw1Glyph       d1g;
+    SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache);
+    TextMapState       tms(*matrix, constY);
+    TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
+
+    if (cache->isSubpixel()) {
+        // maybe we should skip the rounding if linearText is set
+        SkAxisAlignment roundBaseline = SkComputeAxisAlignmentForHText(*matrix);
+
+        if (SkPaint::kLeft_Align == paint.getTextAlign()) {
+            while (text < stop) {
+
+                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;
+
+                if (kX_SkAxisAlignment == roundBaseline) {
+                    fyMask = 0;
+                } else if (kY_SkAxisAlignment == roundBaseline) {
+                    fxMask = 0;
+                }
+
+                const SkGlyph& glyph = glyphCacheProc(cache, &text,
+                                                      fx & fxMask, fy & fyMask);
+
+                if (glyph.fWidth) {
+                    proc(d1g, fx, fy, glyph);
+                }
+                pos += scalarsPerPosition;
+            }
+        } else {
+            while (text < stop) {
+                const char* currentText = text;
+                const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0);
+
+                if (glyph->fWidth) {
+                    SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;)
+                    SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;)
+
+                    SkFixed fx, fy;
+                    SkFixed fxMask = ~0;
+                    SkFixed fyMask = ~0;
+                    tmsProc(tms, pos);
+
+                    {
+                        SkIPoint fixedLoc;
+                        alignProc(tms.fLoc, *glyph, &fixedLoc);
+                        fx = fixedLoc.fX + (SK_FixedHalf >> SkGlyph::kSubBits);
+                        fy = fixedLoc.fY + (SK_FixedHalf >> SkGlyph::kSubBits);
+
+                        if (kX_SkAxisAlignment == roundBaseline) {
+                            fyMask = 0;
+                        } else if (kY_SkAxisAlignment == roundBaseline) {
+                            fxMask = 0;
+                        }
+                    }
+
+                    // have to call again, now that we've been "aligned"
+                    glyph = &glyphCacheProc(cache, &currentText,
+                                            fx & fxMask, fy & fyMask);
+                    // the assumption is that the advance hasn't changed
+                    SkASSERT(prevAdvX == glyph->fAdvanceX);
+                    SkASSERT(prevAdvY == glyph->fAdvanceY);
+
+                    proc(d1g, fx, fy, *glyph);
+                }
+                pos += scalarsPerPosition;
+            }
+        }
+    } else {    // not subpixel
+        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;
+        }
+    }
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPathMeasure.h"
+
+static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
+                        SkPathMeasure& meas, const SkMatrix& matrix) {
+    SkMatrix::MapXYProc proc = matrix.getMapXYProc();
+
+    for (int i = 0; i < count; i++) {
+        SkPoint pos;
+        SkVector tangent;
+
+        proc(matrix, src[i].fX, src[i].fY, &pos);
+        SkScalar sx = pos.fX;
+        SkScalar sy = pos.fY;
+
+        meas.getPosTan(sx, &pos, &tangent);
+
+        /*  This is the old way (that explains our approach but is way too slow
+            SkMatrix    matrix;
+            SkPoint     pt;
+
+            pt.set(sx, sy);
+            matrix.setSinCos(tangent.fY, tangent.fX);
+            matrix.preTranslate(-sx, 0);
+            matrix.postTranslate(pos.fX, pos.fY);
+            matrix.mapPoints(&dst[i], &pt, 1);
+        */
+        dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
+                   pos.fY + SkScalarMul(tangent.fX, sy));
+    }
+}
+
+/*  TODO
+
+    Need differentially more subdivisions when the follow-path is curvy. Not sure how to
+    determine that, but we need it. I guess a cheap answer is let the caller tell us,
+    but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
+*/
+static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
+                      const SkMatrix& matrix) {
+    SkPath::Iter    iter(src, false);
+    SkPoint         srcP[4], dstP[3];
+    SkPath::Verb    verb;
+
+    while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                morphpoints(dstP, srcP, 1, meas, matrix);
+                dst->moveTo(dstP[0]);
+                break;
+            case SkPath::kLine_Verb:
+                // turn lines into quads to look bendy
+                srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
+                srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
+                morphpoints(dstP, srcP, 2, meas, matrix);
+                dst->quadTo(dstP[0], dstP[1]);
+                break;
+            case SkPath::kQuad_Verb:
+                morphpoints(dstP, &srcP[1], 2, meas, matrix);
+                dst->quadTo(dstP[0], dstP[1]);
+                break;
+            case SkPath::kCubic_Verb:
+                morphpoints(dstP, &srcP[1], 3, meas, matrix);
+                dst->cubicTo(dstP[0], dstP[1], dstP[2]);
+                break;
+            case SkPath::kClose_Verb:
+                dst->close();
+                break;
+            default:
+                SkDEBUGFAIL("unknown verb");
+                break;
+        }
+    }
+}
+
+void SkDraw::drawTextOnPath(const char text[], size_t byteLength,
+                            const SkPath& follow, const SkMatrix* matrix,
+                            const SkPaint& paint) const {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
+        return;
+    }
+
+    SkTextToPathIter    iter(text, byteLength, paint, true, true);
+    SkPathMeasure       meas(follow, false);
+    SkScalar            hOffset = 0;
+
+    // need to measure first
+    if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkScalar pathLen = meas.getLength();
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            pathLen = SkScalarHalf(pathLen);
+        }
+        hOffset += pathLen;
+    }
+
+    const SkPath*   iterPath;
+    SkScalar        xpos;
+    SkMatrix        scaledMatrix;
+    SkScalar        scale = iter.getPathScale();
+
+    scaledMatrix.setScale(scale, scale);
+
+    while ((iterPath = iter.next(&xpos)) != NULL) {
+        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);
+        }
+    }
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+void SkDraw::drawPosTextOnPath(const char text[], size_t byteLength,
+                               const SkPoint pos[], const SkPaint& paint,
+                               const SkPath& path, const SkMatrix* matrix) const {
+    // nothing to draw
+    if (text == NULL || byteLength == 0 || fRC->isEmpty()) {
+        return;
+    }
+
+    SkMatrix scaledMatrix;
+    SkPathMeasure meas(path, false);
+
+    SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(
+            SkPaint::kForward_TextBufferDirection, true);
+
+    // Copied (modified) from SkTextToPathIter constructor to setup paint
+    SkPaint tempPaint(paint);
+
+    tempPaint.setLinearText(true);
+    tempPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
+
+    if (tempPaint.getPathEffect() == NULL && !(tempPaint.getStrokeWidth() > 0
+            && tempPaint.getStyle() != SkPaint::kFill_Style)) {
+        tempPaint.setStyle(SkPaint::kFill_Style);
+        tempPaint.setPathEffect(NULL);
+    }
+    // End copied from SkTextToPathIter constructor
+
+    // detach cache
+    SkGlyphCache* cache = tempPaint.detachCache(NULL);
+
+    // Must set scale, even if 1
+    SkScalar scale = SK_Scalar1;
+    scaledMatrix.setScale(scale, scale);
+
+    // Loop over all glyph ids
+    for (const char* stop = text + byteLength; text < stop; pos++) {
+
+        const SkGlyph& glyph = glyphCacheProc(cache, &text);
+        SkPath tmp;
+
+        const SkPath* glyphPath = cache->findPath(glyph);
+        if (glyphPath == NULL) {
+            continue;
+        }
+
+        SkMatrix m(scaledMatrix);
+        m.postTranslate(pos->fX, 0);
+
+        if (matrix) {
+            m.postConcat(*matrix);
+        }
+
+        morphpath(&tmp, *glyphPath, meas, m);
+        this->drawPath(tmp, tempPaint);
+
+    }
+
+    // re-attach cache
+    SkGlyphCache::AttachCache(cache);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct VertState {
+    int f0, f1, f2;
+
+    VertState(int vCount, const uint16_t indices[], int indexCount)
+            : fIndices(indices) {
+        fCurrIndex = 0;
+        if (indices) {
+            fCount = indexCount;
+        } else {
+            fCount = vCount;
+        }
+    }
+
+    typedef bool (*Proc)(VertState*);
+    Proc chooseProc(SkCanvas::VertexMode mode);
+
+private:
+    int             fCount;
+    int             fCurrIndex;
+    const uint16_t* fIndices;
+
+    static bool Triangles(VertState*);
+    static bool TrianglesX(VertState*);
+    static bool TriangleStrip(VertState*);
+    static bool TriangleStripX(VertState*);
+    static bool TriangleFan(VertState*);
+    static bool TriangleFanX(VertState*);
+};
+
+bool VertState::Triangles(VertState* state) {
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = index + 0;
+    state->f1 = index + 1;
+    state->f2 = index + 2;
+    state->fCurrIndex = index + 3;
+    return true;
+}
+
+bool VertState::TrianglesX(VertState* state) {
+    const uint16_t* indices = state->fIndices;
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = indices[index + 0];
+    state->f1 = indices[index + 1];
+    state->f2 = indices[index + 2];
+    state->fCurrIndex = index + 3;
+    return true;
+}
+
+bool VertState::TriangleStrip(VertState* state) {
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f2 = index + 2;
+    if (index & 1) {
+        state->f0 = index + 1;
+        state->f1 = index + 0;
+    } else {
+        state->f0 = index + 0;
+        state->f1 = index + 1;
+    }
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+bool VertState::TriangleStripX(VertState* state) {
+    const uint16_t* indices = state->fIndices;
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f2 = indices[index + 2];
+    if (index & 1) {
+        state->f0 = indices[index + 1];
+        state->f1 = indices[index + 0];
+    } else {
+        state->f0 = indices[index + 0];
+        state->f1 = indices[index + 1];
+    }
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+bool VertState::TriangleFan(VertState* state) {
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = 0;
+    state->f1 = index + 1;
+    state->f2 = index + 2;
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+bool VertState::TriangleFanX(VertState* state) {
+    const uint16_t* indices = state->fIndices;
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = indices[0];
+    state->f1 = indices[index + 1];
+    state->f2 = indices[index + 2];
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) {
+    switch (mode) {
+        case SkCanvas::kTriangles_VertexMode:
+            return fIndices ? TrianglesX : Triangles;
+        case SkCanvas::kTriangleStrip_VertexMode:
+            return fIndices ? TriangleStripX : TriangleStrip;
+        case SkCanvas::kTriangleFan_VertexMode:
+            return fIndices ? TriangleFanX : TriangleFan;
+        default:
+            return NULL;
+    }
+}
+
+typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRasterClip&,
+                         SkBlitter*);
+
+static HairProc ChooseHairProc(bool doAntiAlias) {
+    return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
+}
+
+static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
+                              const SkPoint texs[], SkMatrix* matrix) {
+    SkPoint src[3], dst[3];
+
+    src[0] = texs[state.f0];
+    src[1] = texs[state.f1];
+    src[2] = texs[state.f2];
+    dst[0] = verts[state.f0];
+    dst[1] = verts[state.f1];
+    dst[2] = verts[state.f2];
+    return matrix->setPolyToPoly(src, dst, 3);
+}
+
+class SkTriColorShader : public SkShader {
+public:
+    SkTriColorShader() {}
+
+    bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+
+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;
+};
+
+bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[],
+                             int index0, int index1, int index2) {
+
+    fColors[0] = SkPreMultiplyColor(colors[index0]);
+    fColors[1] = SkPreMultiplyColor(colors[index1]);
+    fColors[2] = SkPreMultiplyColor(colors[index2]);
+
+    SkMatrix m, im;
+    m.reset();
+    m.set(0, pts[index1].fX - pts[index0].fX);
+    m.set(1, pts[index2].fX - pts[index0].fX);
+    m.set(2, pts[index0].fX);
+    m.set(3, pts[index1].fY - pts[index0].fY);
+    m.set(4, pts[index2].fY - pts[index0].fY);
+    m.set(5, pts[index0].fY);
+    if (!m.invert(&im)) {
+        return false;
+    }
+    return fDstToUnit.setConcat(im, this->getTotalInverse());
+}
+
+#include "SkColorPriv.h"
+#include "SkComposeShader.h"
+
+static int ScalarTo256(SkScalar v) {
+    int scale = SkScalarToFixed(v) >> 8;
+    if (scale < 0) {
+        scale = 0;
+    }
+    if (scale > 255) {
+        scale = 255;
+    }
+    return SkAlpha255To256(scale);
+}
+
+void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
+    SkPoint src;
+
+    for (int i = 0; i < count; i++) {
+        fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
+        x += 1;
+
+        int scale1 = ScalarTo256(src.fX);
+        int scale2 = ScalarTo256(src.fY);
+        int scale0 = 256 - scale1 - scale2;
+        if (scale0 < 0) {
+            if (scale1 > scale2) {
+                scale2 = 256 - scale1;
+            } else {
+                scale1 = 256 - scale2;
+            }
+            scale0 = 0;
+        }
+
+        dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
+        SkAlphaMulQ(fColors[1], scale1) +
+        SkAlphaMulQ(fColors[2], scale2);
+    }
+}
+
+void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
+                          const SkPoint vertices[], const SkPoint textures[],
+                          const SkColor colors[], SkXfermode* xmode,
+                          const uint16_t indices[], int indexCount,
+                          const SkPaint& paint) const {
+    SkASSERT(0 == count || NULL != vertices);
+
+    // abort early if there is nothing to draw
+    if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
+        return;
+    }
+
+    // transform out vertices into device coordinates
+    SkAutoSTMalloc<16, SkPoint> storage(count);
+    SkPoint* devVerts = storage.get();
+    fMatrix->mapPoints(devVerts, vertices, count);
+
+    if (fBounder) {
+        SkRect bounds;
+        bounds.set(devVerts, count);
+        if (!fBounder->doRect(bounds, paint)) {
+            return;
+        }
+    }
+
+    /*
+        We can draw the vertices in 1 of 4 ways:
+
+        - solid color (no shader/texture[], no colors[])
+        - just colors (no shader/texture[], has colors[])
+        - just texture (has shader/texture[], no colors[])
+        - colors * texture (has shader/texture[], has colors[])
+
+        Thus for texture drawing, we need both texture[] and a shader.
+    */
+
+    SkTriColorShader triShader; // must be above declaration of p
+    SkPaint p(paint);
+
+    SkShader* shader = p.getShader();
+    if (NULL == shader) {
+        // if we have no shader, we ignore the texture coordinates
+        textures = NULL;
+    } else if (NULL == textures) {
+        // if we don't have texture coordinates, ignore the shader
+        p.setShader(NULL);
+        shader = NULL;
+    }
+
+    // setup the custom shader (if needed)
+    if (NULL != colors) {
+        if (NULL == textures) {
+            // just colors (no texture)
+            p.setShader(&triShader);
+        } else {
+            // colors * texture
+            SkASSERT(shader);
+            bool releaseMode = false;
+            if (NULL == xmode) {
+                xmode = SkXfermode::Create(SkXfermode::kMultiply_Mode);
+                releaseMode = true;
+            }
+            SkShader* compose = SkNEW_ARGS(SkComposeShader,
+                                           (&triShader, shader, xmode));
+            p.setShader(compose)->unref();
+            if (releaseMode) {
+                xmode->unref();
+            }
+        }
+    }
+
+    SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p);
+    // 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;
+            }
+        }
+
+        while (vertProc(&state)) {
+            if (NULL != textures) {
+                if (texture_to_matrix(state, vertices, textures, &tempM)) {
+                    if (hasLocalM) {
+                        tempM.postConcat(localM);
+                    }
+                    shader->setLocalMatrix(tempM);
+                    // need to recal setContext since we changed the local matrix
+                    if (!shader->setContext(*fBitmap, p, *fMatrix)) {
+                        continue;
+                    }
+                }
+            }
+            if (NULL != colors) {
+                if (!triShader.setup(vertices, colors,
+                                     state.f0, state.f1, state.f2)) {
+                    continue;
+                }
+            }
+
+            SkPoint tmp[] = {
+                devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
+            };
+            SkScan::FillTriangle(tmp, *fRC, blitter.get());
+        }
+        // now restore the shader's original local matrix
+        if (NULL != shader) {
+            if (hasLocalM) {
+                shader->setLocalMatrix(localM);
+            } else {
+                shader->resetLocalMatrix();
+            }
+        }
+    } else {
+        // no colors[] and no texture
+        HairProc hairProc = ChooseHairProc(paint.isAntiAlias());
+        const SkRasterClip& clip = *fRC;
+        while (vertProc(&state)) {
+            hairProc(devVerts[state.f0], devVerts[state.f1], clip, blitter.get());
+            hairProc(devVerts[state.f1], devVerts[state.f2], clip, blitter.get());
+            hairProc(devVerts[state.f2], devVerts[state.f0], clip, blitter.get());
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkDraw::validate() const {
+    SkASSERT(fBitmap != NULL);
+    SkASSERT(fMatrix != NULL);
+    SkASSERT(fClip != NULL);
+    SkASSERT(fRC != NULL);
+
+    const SkIRect&  cr = fRC->getBounds();
+    SkIRect         br;
+
+    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
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBounder::SkBounder() {
+    // initialize up front. This gets reset by SkCanvas before each draw call.
+    fClip = &SkRegion::GetEmptyRegion();
+}
+
+bool SkBounder::doIRect(const SkIRect& r) {
+    SkIRect    rr;
+    return rr.intersect(fClip->getBounds(), r) && this->onIRect(rr);
+}
+
+// TODO: change the prototype to take fixed, and update the callers
+bool SkBounder::doIRectGlyph(const SkIRect& r, int x, int y,
+                             const SkGlyph& glyph) {
+    SkIRect    rr;
+    if (!rr.intersect(fClip->getBounds(), r)) {
+        return false;
+    }
+    GlyphRec rec;
+    rec.fLSB.set(SkIntToFixed(x), SkIntToFixed(y));
+    rec.fRSB.set(rec.fLSB.fX + glyph.fAdvanceX,
+                 rec.fLSB.fY + glyph.fAdvanceY);
+    rec.fGlyphID = glyph.getGlyphID();
+    rec.fFlags = 0;
+    return this->onIRectGlyph(rr, rec);
+}
+
+bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1,
+                           const SkPaint& paint) {
+    SkIRect     r;
+    SkScalar    v0, v1;
+
+    v0 = pt0.fX;
+    v1 = pt1.fX;
+    if (v0 > v1) {
+        SkTSwap<SkScalar>(v0, v1);
+    }
+    r.fLeft     = SkScalarFloor(v0);
+    r.fRight    = SkScalarCeil(v1);
+
+    v0 = pt0.fY;
+    v1 = pt1.fY;
+    if (v0 > v1) {
+        SkTSwap<SkScalar>(v0, v1);
+    }
+    r.fTop      = SkScalarFloor(v0);
+    r.fBottom   = SkScalarCeil(v1);
+
+    if (paint.isAntiAlias()) {
+        r.inset(-1, -1);
+    }
+    return this->doIRect(r);
+}
+
+bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint) {
+    SkIRect    r;
+
+    if (paint.getStyle() == SkPaint::kFill_Style) {
+        rect.round(&r);
+    } else {
+        int rad = -1;
+        rect.roundOut(&r);
+        if (paint.isAntiAlias()) {
+            rad = -2;
+        }
+        r.inset(rad, rad);
+    }
+    return this->doIRect(r);
+}
+
+bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) {
+    SkIRect       r;
+    const SkRect& bounds = path.getBounds();
+
+    if (doFill) {
+        bounds.round(&r);
+    } else {    // hairline
+        bounds.roundOut(&r);
+    }
+
+    if (paint.isAntiAlias()) {
+        r.inset(-1, -1);
+    }
+    return this->doIRect(r);
+}
+
+void SkBounder::commit() {
+    // override in subclass
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPath.h"
+#include "SkDraw.h"
+#include "SkRegion.h"
+#include "SkBlitter.h"
+
+static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
+                           SkMaskFilter* filter, const SkMatrix* filterMatrix,
+                           SkIRect* bounds) {
+    if (devPath.isEmpty()) {
+        return false;
+    }
+
+    //  init our bounds from the path
+    {
+        SkRect pathBounds = devPath.getBounds();
+        pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+        pathBounds.roundOut(bounds);
+    }
+
+    SkIPoint margin;
+    if (filter) {
+        SkASSERT(filterMatrix);
+
+        SkMask srcM, dstM;
+
+        srcM.fBounds = *bounds;
+        srcM.fFormat = SkMask::kA8_Format;
+        srcM.fImage = NULL;
+        if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
+            return false;
+        }
+    }
+
+    // (possibly) trim the bounds to reflect the clip
+    // (plus whatever slop the filter needs)
+    if (clipBounds) {
+        SkIRect tmp = *clipBounds;
+        // Ugh. Guard against gigantic margins from wacky filters. Without this
+        // check we can request arbitrary amounts of slop beyond our visible
+        // clip, and bring down the renderer (at least on finite RAM machines
+        // like handsets, etc.). Need to balance this invented value between
+        // quality of large filters like blurs, and the corresponding memory
+        // requests.
+        static const int MAX_MARGIN = 128;
+        tmp.inset(-SkMin32(margin.fX, MAX_MARGIN),
+                  -SkMin32(margin.fY, MAX_MARGIN));
+        if (!bounds->intersect(tmp)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static void draw_into_mask(const SkMask& mask, const SkPath& devPath) {
+    SkBitmap        bm;
+    SkDraw          draw;
+    SkRasterClip    clip;
+    SkMatrix        matrix;
+    SkPaint         paint;
+
+    bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes);
+    bm.setPixels(mask.fImage);
+
+    clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
+    matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
+                        -SkIntToScalar(mask.fBounds.fTop));
+
+    draw.fBitmap    = &bm;
+    draw.fRC        = &clip;
+    draw.fClip      = &clip.bwRgn();
+    draw.fMatrix    = &matrix;
+    draw.fBounder   = NULL;
+    paint.setAntiAlias(true);
+    draw.drawPath(devPath, paint);
+}
+
+bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
+                        SkMaskFilter* filter, const SkMatrix* filterMatrix,
+                        SkMask* mask, SkMask::CreateMode mode) {
+    if (SkMask::kJustRenderImage_CreateMode != mode) {
+        if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
+            return false;
+    }
+
+    if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
+        mask->fFormat = SkMask::kA8_Format;
+        mask->fRowBytes = mask->fBounds.width();
+        size_t size = mask->computeImageSize();
+        if (0 == size) {
+            // we're too big to allocate the mask, abort
+            return false;
+        }
+        mask->fImage = SkMask::AllocImage(size);
+        memset(mask->fImage, 0, mask->computeImageSize());
+    }
+
+    if (SkMask::kJustComputeBounds_CreateMode != mode) {
+        draw_into_mask(*mask, devPath);
+    }
+
+    return true;
+}
diff --git a/legacy/src/core/SkDrawProcs.h b/legacy/src/core/SkDrawProcs.h
new file mode 100644
index 0000000..2e26ecf
--- /dev/null
+++ b/legacy/src/core/SkDrawProcs.h
@@ -0,0 +1,46 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkDrawProcs_DEFINED
+#define SkDrawProcs_DEFINED
+
+#include "SkDraw.h"
+
+class SkAAClip;
+class SkBlitter;
+
+struct SkDraw1Glyph {
+    const SkDraw* fDraw;
+    SkBounder* fBounder;
+    const SkRegion* fClip;
+    const SkAAClip* fAAClip;
+    SkBlitter* fBlitter;
+    SkGlyphCache* fCache;
+    SkIRect fClipBounds;
+
+    // The fixed x,y are pre-rounded, so impls just trunc them down to ints.
+    // 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);
+};
+
+struct SkDrawProcs {
+    SkDraw1Glyph::Proc  fD1GProc;
+};
+
+/**
+ *  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 
+ *  conditions are false, then this returns false and coverage is ignored.
+ */
+bool SkDrawTreatAsHairline(const SkPaint&, const SkMatrix&, SkScalar* coverage);
+
+#endif
+
diff --git a/legacy/src/core/SkEdge.cpp b/legacy/src/core/SkEdge.cpp
new file mode 100644
index 0000000..aab1c76
--- /dev/null
+++ b/legacy/src/core/SkEdge.cpp
@@ -0,0 +1,466 @@
+
+/*
+ * 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 "SkEdge.h"
+#include "SkFDot6.h"
+#include "SkMath.h"
+
+/*
+    In setLine, setQuadratic, setCubic, the first thing we do is to convert
+    the points into FDot6. This is modulated by the shift parameter, which
+    will either be 0, or something like 2 for antialiasing.
+
+    In the float case, we want to turn the float into .6 by saying pt * 64,
+    or pt * 256 for antialiasing. This is implemented as 1 << (shift + 6).
+
+    In the fixed case, we want to turn the fixed into .6 by saying pt >> 10,
+    or pt >> 8 for antialiasing. This is implemented as pt >> (10 - shift).
+*/
+
+/////////////////////////////////////////////////////////////////////////
+
+int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
+                    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;
+    }
+    // are we completely above or below the clip?
+    if (NULL != clip && (top >= clip->fBottom || bot <= clip->fTop)) {
+        return 0;
+    }
+
+    SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
+
+    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63));   // + SK_Fixed1/2
+    fDX         = slope;
+    fFirstY     = top;
+    fLastY      = bot - 1;
+    fCurveCount = 0;
+    fWinding    = SkToS8(winding);
+    fCurveShift = 0;
+
+    if (clip) {
+        this->chopLineWithClip(*clip);
+    }
+    return 1;
+}
+
+// called from a curve subclass
+int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1)
+{
+    SkASSERT(fWinding == 1 || fWinding == -1);
+    SkASSERT(fCurveCount != 0);
+//    SkASSERT(fCurveShift != 0);
+
+    y0 >>= 10;
+    y1 >>= 10;
+
+    SkASSERT(y0 <= y1);
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y1);
+
+//  SkASSERT(top >= fFirstY);
+
+    // are we a zero-height line?
+    if (top == bot)
+        return 0;
+
+    x0 >>= 10;
+    x1 >>= 10;
+
+    SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
+
+    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63));   // + SK_Fixed1/2
+    fDX         = slope;
+    fFirstY     = top;
+    fLastY      = bot - 1;
+
+    return 1;
+}
+
+void SkEdge::chopLineWithClip(const SkIRect& clip)
+{
+    int top = fFirstY;
+
+    SkASSERT(top < clip.fBottom);
+
+    // clip the line to the top
+    if (top < clip.fTop)
+    {
+        SkASSERT(fLastY >= clip.fTop);
+        fX += fDX * (clip.fTop - top);
+        fFirstY = clip.fTop;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64.
+    Note that this limits the number of lines we use to approximate a curve.
+    If we need to increase this, we need to store fCurveCount in something
+    larger than int8_t.
+*/
+#define MAX_COEFF_SHIFT     6
+
+static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy)
+{
+    dx = SkAbs32(dx);
+    dy = SkAbs32(dy);
+    // return max + min/2
+    if (dx > dy)
+        dx += dy >> 1;
+    else
+        dx = dy + (dx >> 1);
+    return dx;
+}
+
+static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy)
+{
+    // cheap calc of distance from center of p0-p2 to the center of the curve
+    SkFDot6 dist = cheap_distance(dx, dy);
+
+    // shift down dist (it is currently in dot6)
+    // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...)
+    // this is chosen by heuristic: make it as big as possible (to minimize segments)
+    // ... but small enough so that our curves still look smooth
+    dist = (dist + (1 << 4)) >> 5;
+
+    // each subdivision (shift value) cuts this dist (error) by 1/4
+    return (32 - SkCLZ(dist)) >> 1;
+}
+
+int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], int shift)
+{
+    SkFDot6 x0, y0, x1, y1, x2, y2;
+
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float scale = float(1 << (shift + 6));
+        x0 = int(pts[0].fX * scale);
+        y0 = int(pts[0].fY * scale);
+        x1 = int(pts[1].fX * scale);
+        y1 = int(pts[1].fY * scale);
+        x2 = int(pts[2].fX * scale);
+        y2 = int(pts[2].fY * scale);
+#else
+        shift = 10 - shift;
+        x0 = pts[0].fX >> shift;
+        y0 = pts[0].fY >> shift;
+        x1 = pts[1].fX >> shift;
+        y1 = pts[1].fY >> shift;
+        x2 = pts[2].fX >> shift;
+        y2 = pts[2].fY >> shift;
+#endif
+    }
+
+    int winding = 1;
+    if (y0 > y2)
+    {
+        SkTSwap(x0, x2);
+        SkTSwap(y0, y2);
+        winding = -1;
+    }
+    SkASSERT(y0 <= y1 && y1 <= y2);
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y2);
+
+    // are we a zero-height quad (line)?
+    if (top == bot)
+        return 0;
+
+    // compute number of steps needed (1 << shift)
+    {
+        SkFDot6 dx = ((x1 << 1) - x0 - x2) >> 2;
+        SkFDot6 dy = ((y1 << 1) - y0 - y2) >> 2;
+        shift = diff_to_shift(dx, dy);
+        SkASSERT(shift >= 0);
+    }
+    // need at least 1 subdivision for our bias trick
+    if (shift == 0) {
+        shift = 1;
+    } 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);
+
+    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);
+
+    fQy     = SkFDot6ToFixed(y0);
+    fQDy    = B + (A >> shift);     // biased by shift
+    fQDDy   = A >> (shift - 1);     // biased by shift
+
+    fQLastX = SkFDot6ToFixed(x2);
+    fQLastY = SkFDot6ToFixed(y2);
+
+    return this->updateQuadratic();
+}
+
+int SkQuadraticEdge::updateQuadratic()
+{
+    int     success;
+    int     count = fCurveCount;
+    SkFixed oldx = fQx;
+    SkFixed oldy = fQy;
+    SkFixed dx = fQDx;
+    SkFixed dy = fQDy;
+    SkFixed newx, newy;
+    int     shift = fCurveShift;
+
+    SkASSERT(count > 0);
+
+    do {
+        if (--count > 0)
+        {
+            newx    = oldx + (dx >> shift);
+            dx    += fQDDx;
+            newy    = oldy + (dy >> shift);
+            dy    += fQDDy;
+        }
+        else    // last segment
+        {
+            newx    = fQLastX;
+            newy    = fQLastY;
+        }
+        success = this->updateLine(oldx, oldy, newx, newy);
+        oldx = newx;
+        oldy = newy;
+    } while (count > 0 && !success);
+
+    fQx         = newx;
+    fQy         = newy;
+    fQDx        = dx;
+    fQDy        = dy;
+    fCurveCount = SkToS8(count);
+    return success;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static inline int SkFDot6UpShift(SkFDot6 x, int upShift) {
+    SkASSERT((x << upShift >> upShift) == x);
+    return x << upShift;
+}
+
+/*  f(1/3) = (8a + 12b + 6c + d) / 27
+    f(2/3) = (a + 6b + 12c + 8d) / 27
+
+    f(1/3)-b = (8a - 15b + 6c + d) / 27
+    f(2/3)-c = (a + 6b - 15c + 8d) / 27
+
+    use 16/512 to approximate 1/27
+*/
+static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d)
+{
+    SkFDot6 oneThird = ((a << 3) - ((b << 4) - b) + 6*c + d) * 19 >> 9;
+    SkFDot6 twoThird = (a + 6*b - ((c << 4) - c) + (d << 3)) * 19 >> 9;
+
+    return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird));
+}
+
+int SkCubicEdge::setCubic(const SkPoint pts[4], const SkIRect* clip, int shift)
+{
+    SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3;
+
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float scale = float(1 << (shift + 6));
+        x0 = int(pts[0].fX * scale);
+        y0 = int(pts[0].fY * scale);
+        x1 = int(pts[1].fX * scale);
+        y1 = int(pts[1].fY * scale);
+        x2 = int(pts[2].fX * scale);
+        y2 = int(pts[2].fY * scale);
+        x3 = int(pts[3].fX * scale);
+        y3 = int(pts[3].fY * scale);
+#else
+        shift = 10 - shift;
+        x0 = pts[0].fX >> shift;
+        y0 = pts[0].fY >> shift;
+        x1 = pts[1].fX >> shift;
+        y1 = pts[1].fY >> shift;
+        x2 = pts[2].fX >> shift;
+        y2 = pts[2].fY >> shift;
+        x3 = pts[3].fX >> shift;
+        y3 = pts[3].fY >> shift;
+#endif
+    }
+
+    int winding = 1;
+    if (y0 > y3)
+    {
+        SkTSwap(x0, x3);
+        SkTSwap(x1, x2);
+        SkTSwap(y0, y3);
+        SkTSwap(y1, y2);
+        winding = -1;
+    }
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y3);
+
+    // are we a zero-height cubic (line)?
+    if (top == bot)
+        return 0;
+
+    // are we completely above or below the clip?
+    if (clip && (top >= clip->fBottom || bot <= clip->fTop))
+        return 0;
+
+    // compute number of steps needed (1 << shift)
+    {
+        // Can't use (center of curve - center of baseline), since center-of-curve
+        // need not be the max delta from the baseline (it could even be coincident)
+        // so we try just looking at the two off-curve points
+        SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3);
+        SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3);
+        // add 1 (by observation)
+        shift = diff_to_shift(dx, dy) + 1;
+    }
+    // need at least 1 subdivision for our bias trick
+    SkASSERT(shift > 0);
+    if (shift > MAX_COEFF_SHIFT) {
+        shift = MAX_COEFF_SHIFT;
+    }
+
+    /*  Since our in coming data is initially shifted down by 10 (or 8 in
+        antialias). That means the most we can shift up is 8. However, we
+        compute coefficients with a 3*, so the safest upshift is really 6
+    */
+    int upShift = 6;    // largest safe value
+    int downShift = shift + upShift - 10;
+    if (downShift < 0) {
+        downShift = 0;
+        upShift = 10 - shift;
+    }
+
+    fWinding    = SkToS8(winding);
+    fCurveCount = SkToS8(-1 << shift);
+    fCurveShift = SkToU8(shift);
+    fCubicDShift = SkToU8(downShift);
+
+    SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift);
+    SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift);
+    SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift);
+
+    fCx     = SkFDot6ToFixed(x0);
+    fCDx    = B + (C >> shift) + (D >> 2*shift);    // biased by shift
+    fCDDx   = 2*C + (3*D >> (shift - 1));           // biased by 2*shift
+    fCDDDx  = 3*D >> (shift - 1);                   // biased by 2*shift
+
+    B = SkFDot6UpShift(3 * (y1 - y0), upShift);
+    C = SkFDot6UpShift(3 * (y0 - y1 - y1 + y2), upShift);
+    D = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift);
+
+    fCy     = SkFDot6ToFixed(y0);
+    fCDy    = B + (C >> shift) + (D >> 2*shift);    // biased by shift
+    fCDDy   = 2*C + (3*D >> (shift - 1));           // biased by 2*shift
+    fCDDDy  = 3*D >> (shift - 1);                   // biased by 2*shift
+
+    fCLastX = SkFDot6ToFixed(x3);
+    fCLastY = SkFDot6ToFixed(y3);
+
+    if (clip)
+    {
+        do {
+            if (!this->updateCubic()) {
+                return 0;
+            }
+        } while (!this->intersectsClip(*clip));
+        this->chopLineWithClip(*clip);
+        return 1;
+    }
+    return this->updateCubic();
+}
+
+int SkCubicEdge::updateCubic()
+{
+    int     success;
+    int     count = fCurveCount;
+    SkFixed oldx = fCx;
+    SkFixed oldy = fCy;
+    SkFixed newx, newy;
+    const int ddshift = fCurveShift;
+    const int dshift = fCubicDShift;
+
+    SkASSERT(count < 0);
+
+    do {
+        if (++count < 0)
+        {
+            newx    = oldx + (fCDx >> dshift);
+            fCDx    += fCDDx >> ddshift;
+            fCDDx   += fCDDDx;
+
+            newy    = oldy + (fCDy >> dshift);
+            fCDy    += fCDDy >> ddshift;
+            fCDDy   += fCDDDy;
+        }
+        else    // last segment
+        {
+        //  SkDebugf("LastX err=%d, LastY err=%d\n", (oldx + (fCDx >> shift) - fLastX), (oldy + (fCDy >> shift) - fLastY));
+            newx    = fCLastX;
+            newy    = fCLastY;
+        }
+        success = this->updateLine(oldx, oldy, newx, newy);
+        oldx = newx;
+        oldy = newy;
+    } while (count < 0 && !success);
+
+    fCx         = newx;
+    fCy         = newy;
+    fCurveCount = SkToS8(count);
+    return success;
+}
+
+
+
diff --git a/legacy/src/core/SkEdge.h b/legacy/src/core/SkEdge.h
new file mode 100644
index 0000000..29a50d1
--- /dev/null
+++ b/legacy/src/core/SkEdge.h
@@ -0,0 +1,85 @@
+
+/*
+ * 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 SkEdge_DEFINED
+#define SkEdge_DEFINED
+
+#include "SkRect.h"
+
+struct SkEdge {
+    enum Type {
+        kLine_Type,
+        kQuad_Type,
+        kCubic_Type
+    };
+
+    SkEdge* fNext;
+    SkEdge* fPrev;
+
+    SkFixed fX;
+    SkFixed fDX;
+    int32_t fFirstY;
+    int32_t fLastY;
+    int8_t fCurveCount;    // only used by kQuad(+) and kCubic(-)
+    uint8_t fCurveShift;    // appled to all Dx/DDx/DDDx except for fCubicDShift exception
+    uint8_t fCubicDShift;   // applied to fCDx and fCDy only in cubic
+    int8_t  fWinding;       // 1 or -1
+
+    int setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
+                int shiftUp);
+    inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by);
+    void chopLineWithClip(const SkIRect& clip);
+
+    inline bool intersectsClip(const SkIRect& clip) const {
+        SkASSERT(fFirstY < clip.fBottom);
+        return fLastY >= clip.fTop;
+    }
+
+#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 {
+        SkASSERT(fPrev && fNext);
+        SkASSERT(fPrev->fNext == this);
+        SkASSERT(fNext->fPrev == this);
+
+        SkASSERT(fFirstY <= fLastY);
+        SkASSERT(SkAbs32(fWinding) == 1);
+    }
+#endif
+};
+
+struct SkQuadraticEdge : public SkEdge {
+    SkFixed fQx, fQy;
+    SkFixed fQDx, fQDy;
+    SkFixed fQDDx, fQDDy;
+    SkFixed fQLastX, fQLastY;
+
+    int setQuadratic(const SkPoint pts[3], int shiftUp);
+    int updateQuadratic();
+};
+
+struct SkCubicEdge : public SkEdge {
+    SkFixed fCx, fCy;
+    SkFixed fCDx, fCDy;
+    SkFixed fCDDx, fCDDy;
+    SkFixed fCDDDx, fCDDDy;
+    SkFixed fCLastX, fCLastY;
+
+    int setCubic(const SkPoint pts[4], const SkIRect* clip, int shiftUp);
+    int updateCubic();
+};
+
+#endif
diff --git a/legacy/src/core/SkEdgeBuilder.cpp b/legacy/src/core/SkEdgeBuilder.cpp
new file mode 100644
index 0000000..01417e4
--- /dev/null
+++ b/legacy/src/core/SkEdgeBuilder.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 "SkEdgeBuilder.h"
+#include "SkPath.h"
+#include "SkEdge.h"
+#include "SkEdgeClipper.h"
+#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)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkEdgeBuilder::addLine(const SkPoint pts[]) {
+    SkEdge* edge = typedAllocThrow<SkEdge>(fAlloc);
+    if (edge->setLine(pts[0], pts[1], NULL, fShiftUp)) {
+        fList.push(edge);
+    } else {
+        // TODO: unallocate edge from storage...
+    }
+}
+
+void SkEdgeBuilder::addQuad(const SkPoint pts[]) {
+    SkQuadraticEdge* edge = typedAllocThrow<SkQuadraticEdge>(fAlloc);
+    if (edge->setQuadratic(pts, fShiftUp)) {
+        fList.push(edge);
+    } else {
+        // TODO: unallocate edge from storage...
+    }
+}
+
+void SkEdgeBuilder::addCubic(const SkPoint pts[]) {
+    SkCubicEdge* edge = typedAllocThrow<SkCubicEdge>(fAlloc);
+    if (edge->setCubic(pts, NULL, fShiftUp)) {
+        fList.push(edge);
+    } else {
+        // TODO: unallocate edge from storage...
+    }
+}
+
+void SkEdgeBuilder::addClipper(SkEdgeClipper* clipper) {
+    SkPoint      pts[4];
+    SkPath::Verb verb;
+
+    while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kLine_Verb:
+                this->addLine(pts);
+                break;
+            case SkPath::kQuad_Verb:
+                this->addQuad(pts);
+                break;
+            case SkPath::kCubic_Verb:
+                this->addCubic(pts);
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setShiftedClip(SkRect* dst, const SkIRect& src, int shift) {
+    dst->set(SkIntToScalar(src.fLeft >> shift),
+             SkIntToScalar(src.fTop >> shift),
+             SkIntToScalar(src.fRight >> shift),
+             SkIntToScalar(src.fBottom >> shift));
+}
+
+int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip,
+                         int shiftUp) {
+    fAlloc.reset();
+    fList.reset();
+    fShiftUp = shiftUp;
+
+    SkPath::Iter    iter(path, true);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    if (iclip) {
+        SkRect clip;
+        setShiftedClip(&clip, *iclip, shiftUp);
+        SkEdgeClipper clipper;
+
+        while ((verb = iter.next(pts)) != 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);
+                    for (int i = 0; i < lineCount; i++) {
+                        this->addLine(&lines[i]);
+                    }
+                    break;
+                }
+                case SkPath::kQuad_Verb:
+                    if (clipper.clipQuad(pts, clip)) {
+                        this->addClipper(&clipper);
+                    }
+                    break;
+                case SkPath::kCubic_Verb:
+                    if (clipper.clipCubic(pts, clip)) {
+                        this->addClipper(&clipper);
+                    }
+                    break;
+                default:
+                    SkDEBUGFAIL("unexpected verb");
+                    break;
+            }
+        }
+    } else {
+        while ((verb = iter.next(pts)) != 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:
+                    this->addLine(pts);
+                    break;
+                case SkPath::kQuad_Verb: {
+                    SkPoint monoX[5];
+                    int n = SkChopQuadAtYExtrema(pts, monoX);
+                    for (int i = 0; i <= n; i++) {
+                        this->addQuad(&monoX[i * 2]);
+                    }
+                    break;
+                }
+                case SkPath::kCubic_Verb: {
+                    SkPoint monoY[10];
+                    int n = SkChopCubicAtYExtrema(pts, monoY);
+                    for (int i = 0; i <= n; i++) {
+                        this->addCubic(&monoY[i * 3]);
+                    }
+                    break;
+                }
+                default:
+                    SkDEBUGFAIL("unexpected verb");
+                    break;
+            }
+        }
+    }
+    return fList.count();
+}
+
+
diff --git a/legacy/src/core/SkEdgeBuilder.h b/legacy/src/core/SkEdgeBuilder.h
new file mode 100644
index 0000000..ae21f05
--- /dev/null
+++ b/legacy/src/core/SkEdgeBuilder.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 SkEdgeBuilder_DEFINED
+#define SkEdgeBuilder_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkRect.h"
+#include "SkTDArray.h"
+
+struct SkEdge;
+class SkEdgeClipper;
+class SkPath;
+
+class SkEdgeBuilder {
+public:
+    SkEdgeBuilder();
+    
+    int build(const SkPath& path, const SkIRect* clip, int shiftUp);
+
+    SkEdge** edgeList() { return fList.begin(); }
+
+private:
+    SkChunkAlloc        fAlloc;
+    SkTDArray<SkEdge*>  fList;
+    int                 fShiftUp;
+
+    void addLine(const SkPoint pts[]);
+    void addQuad(const SkPoint pts[]);
+    void addCubic(const SkPoint pts[]);
+    void addClipper(SkEdgeClipper*);
+};
+
+#endif
diff --git a/legacy/src/core/SkEdgeClipper.cpp b/legacy/src/core/SkEdgeClipper.cpp
new file mode 100644
index 0000000..d77f6f8
--- /dev/null
+++ b/legacy/src/core/SkEdgeClipper.cpp
@@ -0,0 +1,524 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkEdgeClipper.h"
+#include "SkGeometry.h"
+
+static bool quick_reject(const SkRect& bounds, const SkRect& clip) {
+    return bounds.fTop >= clip.fBottom || bounds.fBottom <= clip.fTop;
+}
+
+static inline void clamp_le(SkScalar& value, SkScalar max) {
+    if (value > max) {
+        value = max;
+    }
+}
+
+static inline void clamp_ge(SkScalar& value, SkScalar min) {
+    if (value < min) {
+        value = min;
+    }
+}
+
+/*  src[] must be monotonic in Y. This routine copies src into dst, and sorts
+ it to be increasing in Y. If it had to reverse the order of the points,
+ it returns true, otherwise it returns false
+ */
+static bool sort_increasing_Y(SkPoint dst[], const SkPoint src[], int count) {
+    // we need the data to be monotonically increasing in Y
+    if (src[0].fY > src[count - 1].fY) {
+        for (int i = 0; i < count; i++) {
+            dst[i] = src[count - i - 1];
+        }
+        return true;
+    } else {
+        memcpy(dst, src, count * sizeof(SkPoint));
+        return false;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2,
+                           SkScalar target, SkScalar* t) {
+    /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2
+     *  We solve for t, using quadratic equation, hence we have to rearrange
+     * our cooefficents to look like At^2 + Bt + C
+     */
+    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) {
+        *t = roots[0];
+        return true;
+    }
+    return false;
+}
+
+static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) {
+    return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t);
+}
+
+static bool chopMonoQuadAtX(SkPoint pts[3], SkScalar x, SkScalar* t) {
+    return chopMonoQuadAt(pts[0].fX, pts[1].fX, pts[2].fX, x, t);
+}
+
+// Modify pts[] in place so that it is clipped in Y to the clip rect
+static void chop_quad_in_Y(SkPoint pts[3], const SkRect& clip) {
+    SkScalar t;
+    SkPoint tmp[5]; // for SkChopQuadAt
+
+    // are we partially above
+    if (pts[0].fY < clip.fTop) {
+        if (chopMonoQuadAtY(pts, clip.fTop, &t)) {
+            // take the 2nd chopped quad
+            SkChopQuadAt(pts, tmp, t);
+            clamp_ge(tmp[2].fY, clip.fTop);
+            clamp_ge(tmp[3].fY, clip.fTop);
+            pts[0] = tmp[2];
+            pts[1] = tmp[3];
+        } else {
+            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the top
+            for (int i = 0; i < 3; i++) {
+                if (pts[i].fY < clip.fTop) {
+                    pts[i].fY = clip.fTop;
+                }
+            }
+        }
+    }
+    
+    // are we partially below
+    if (pts[2].fY > clip.fBottom) {
+        if (chopMonoQuadAtY(pts, clip.fBottom, &t)) {
+            SkChopQuadAt(pts, tmp, t);
+            clamp_le(tmp[1].fY, clip.fBottom);
+            clamp_le(tmp[2].fY, clip.fBottom);
+            pts[1] = tmp[1];
+            pts[2] = tmp[2];
+        } else {
+            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the bottom
+            for (int i = 0; i < 3; i++) {
+                if (pts[i].fY > clip.fBottom) {
+                    pts[i].fY = clip.fBottom;
+                }
+            }
+        }
+    }
+}
+
+// srcPts[] must be monotonic in X and Y
+void SkEdgeClipper::clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip) {
+    SkPoint pts[3];
+    bool reverse = sort_increasing_Y(pts, srcPts, 3);
+
+    // are we completely above or below
+    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);
+
+    if (pts[0].fX > pts[2].fX) {
+        SkTSwap<SkPoint>(pts[0], pts[2]);
+        reverse = !reverse;
+    }
+    SkASSERT(pts[0].fX <= pts[1].fX);
+    SkASSERT(pts[1].fX <= pts[2].fX);
+
+    // Now chop in X has needed, and record the segments
+
+    if (pts[2].fX <= clip.fLeft) {  // wholly to the left
+        this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
+        return;
+    }
+    if (pts[0].fX >= clip.fRight) {  // wholly to the right
+        this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
+        return;
+    }
+
+    SkScalar t;
+    SkPoint tmp[5]; // for SkChopQuadAt
+
+    // are we partially to the left
+    if (pts[0].fX < clip.fLeft) {
+        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_ge(tmp[3].fX, clip.fLeft);
+            pts[0] = tmp[2];
+            pts[1] = tmp[3];
+        } else {
+            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the left
+            this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
+            return;
+        }
+    }
+    
+    // are we partially to the right
+    if (pts[2].fX > clip.fRight) {
+        if (chopMonoQuadAtX(pts, clip.fRight, &t)) {
+            SkChopQuadAt(pts, tmp, t);
+            clamp_le(tmp[1].fX, clip.fRight);
+            clamp_le(tmp[2].fX, clip.fRight);
+            this->appendQuad(tmp, reverse);
+            this->appendVLine(clip.fRight, tmp[2].fY, tmp[4].fY, reverse);
+        } else {
+            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the right
+            this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
+        }
+    } else {    // wholly inside the clip
+        this->appendQuad(pts, reverse);
+    }
+}
+
+bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) {
+    fCurrPoint = fPoints;
+    fCurrVerb = fVerbs;
+
+    SkRect  bounds;
+    bounds.set(srcPts, 3);
+    
+    if (!quick_reject(bounds, clip)) {
+        SkPoint monoY[5];
+        int countY = SkChopQuadAtYExtrema(srcPts, monoY);
+        for (int y = 0; y <= countY; y++) {
+            SkPoint monoX[5];
+            int countX = SkChopQuadAtXExtrema(&monoY[y * 2], monoX);
+            for (int x = 0; x <= countX; x++) {
+                this->clipMonoQuad(&monoX[x * 2], clip);
+                SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
+                SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
+            }
+        }
+    }
+
+    *fCurrVerb = SkPath::kDone_Verb;
+    fCurrPoint = fPoints;
+    fCurrVerb = fVerbs;
+    return SkPath::kDone_Verb != fVerbs[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
+                                 SkScalar D, SkScalar t) {
+    return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
+}
+
+/*  Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the
+    t value such that cubic(t) = target
+ */
+static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
+                           SkScalar target, SkScalar* t) {
+ //   SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3);
+    SkASSERT(c0 < target && target < c3);
+
+    SkScalar D = c0 - target;
+    SkScalar A = c3 + 3*(c1 - c2) - c0;
+    SkScalar B = 3*(c2 - c1 - c1 + c0);
+    SkScalar C = 3*(c1 - c0);
+
+    const SkScalar TOLERANCE = SK_Scalar1 / 4096;
+    SkScalar minT = 0;
+    SkScalar maxT = SK_Scalar1;
+    SkScalar mid;
+    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;
+//    SkDebugf("-- evalCubicAt %d delta %g\n", i, eval_cubic_coeff(A, B, C, D, *t));
+    return true;
+}
+
+static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) {
+    return chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, t);
+}
+
+static bool chopMonoCubicAtX(SkPoint pts[4], SkScalar x, SkScalar* t) {
+    return chopMonoCubicAt(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, x, t);
+}
+
+// Modify pts[] in place so that it is clipped in Y to the clip rect
+static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
+    
+    // are we partially above
+    if (pts[0].fY < clip.fTop) {
+        SkScalar t;
+        if (chopMonoCubicAtY(pts, clip.fTop, &t)) {
+            SkPoint tmp[7];
+            SkChopCubicAt(pts, tmp, t);
+
+            // tmp[3, 4, 5].fY should all be to the below clip.fTop, 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);
+
+            pts[0] = tmp[3];
+            pts[1] = tmp[4];
+            pts[2] = tmp[5];
+        } else {
+            // if chopMonoCubicAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the top
+            for (int i = 0; i < 4; i++) {
+                clamp_ge(pts[i].fY, clip.fTop);
+            }
+        }
+    }
+    
+    // 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);
+            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];
+        } else {
+            // if chopMonoCubicAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the bottom
+            for (int i = 0; i < 4; i++) {
+                clamp_le(pts[i].fY, clip.fBottom);
+            }
+        }
+    }
+}
+
+// srcPts[] must be monotonic in X and Y
+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;
+    }
+
+    // Now chop so that pts is contained within clip in Y
+    chop_cubic_in_Y(pts, clip);
+
+    if (pts[0].fX > pts[3].fX) {
+        SkTSwap<SkPoint>(pts[0], pts[3]);
+        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;
+    }
+    if (pts[0].fX >= clip.fRight) {  // wholly to the right
+        this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
+        return;
+    }
+
+    // are we partially to the left
+    if (pts[0].fX < clip.fLeft) {
+        SkScalar t;
+        if (chopMonoCubicAtX(pts, clip.fLeft, &t)) {
+            SkPoint tmp[7];
+            SkChopCubicAt(pts, tmp, t);
+            this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
+
+            // tmp[3, 4, 5].fX should all be to the right of clip.fLeft, and
+            // 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);
+
+            pts[0] = tmp[3];
+            pts[1] = tmp[4];
+            pts[2] = tmp[5];
+        } else {
+            // if chopMonocubicAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the left
+            this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
+            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);
+            clamp_le(tmp[2].fX, clip.fRight);
+            clamp_le(tmp[3].fX, clip.fRight);
+            this->appendCubic(tmp, reverse);
+            this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
+        } else {
+            // if chopMonoCubicAtX failed, then we may have hit inexact numerics
+            // so we just clamp against the right
+            this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
+        }
+    } else {    // wholly inside the clip
+        this->appendCubic(pts, reverse);
+    }
+}
+
+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);
+        for (int y = 0; y <= countY; y++) {
+        //    sk_assert_monotonic_y(&monoY[y * 3], 4);
+            SkPoint monoX[10];
+            int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX);
+            for (int x = 0; x <= countX; x++) {
+            //    sk_assert_monotonic_y(&monoX[x * 3], 4);
+            //    sk_assert_monotonic_x(&monoX[x * 3], 4);
+                this->clipMonoCubic(&monoX[x * 3], clip);
+                SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
+                SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
+            }
+        }
+    }
+    
+    *fCurrVerb = SkPath::kDone_Verb;
+    fCurrPoint = fPoints;
+    fCurrVerb = fVerbs;
+    return SkPath::kDone_Verb != fVerbs[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1,
+                                bool reverse) {
+    *fCurrVerb++ = SkPath::kLine_Verb;
+    
+    if (reverse) {
+        SkTSwap<SkScalar>(y0, y1);
+    }
+    fCurrPoint[0].set(x, y0);
+    fCurrPoint[1].set(x, y1);
+    fCurrPoint += 2;
+}
+
+void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) {
+    *fCurrVerb++ = SkPath::kQuad_Verb;
+    
+    if (reverse) {
+        fCurrPoint[0] = pts[2];
+        fCurrPoint[2] = pts[0];
+    } else {
+        fCurrPoint[0] = pts[0];
+        fCurrPoint[2] = pts[2];
+    }
+    fCurrPoint[1] = pts[1];
+    fCurrPoint += 3;
+}
+
+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];
+        }
+    } else {
+        memcpy(fCurrPoint, pts, 4 * sizeof(SkPoint));
+    }
+    fCurrPoint += 4;
+}
+
+SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) {
+    SkPath::Verb verb = *fCurrVerb;
+
+    switch (verb) {
+        case SkPath::kLine_Verb:
+            memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint));
+            fCurrPoint += 2;
+            fCurrVerb += 1;
+            break;
+        case SkPath::kQuad_Verb:
+            memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint));
+            fCurrPoint += 3;
+            fCurrVerb += 1;
+            break;
+        case SkPath::kCubic_Verb:
+            memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint));
+            fCurrPoint += 4;
+            fCurrVerb += 1;
+            break;
+        case SkPath::kDone_Verb:
+            break;
+        default:
+            SkDEBUGFAIL("unexpected verb in quadclippper2 iter");
+            break;
+    }
+    return verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+static void assert_monotonic(const SkScalar coord[], int count) {
+    if (coord[0] > coord[(count - 1) * 2]) {
+        for (int i = 1; i < count; i++) {
+            SkASSERT(coord[2 * (i - 1)] >= coord[i * 2]);
+        }
+    } else if (coord[0] < coord[(count - 1) * 2]) {
+        for (int i = 1; i < count; i++) {
+            SkASSERT(coord[2 * (i - 1)] <= coord[i * 2]);
+        }
+    } else {
+        for (int i = 1; i < count; i++) {
+            SkASSERT(coord[2 * (i - 1)] == coord[i * 2]);
+        }
+    }
+}
+
+void sk_assert_monotonic_y(const SkPoint pts[], int count) {
+    if (count > 1) {
+        assert_monotonic(&pts[0].fY, count);
+    }
+}
+
+void sk_assert_monotonic_x(const SkPoint pts[], int count) {
+    if (count > 1) {
+        assert_monotonic(&pts[0].fX, count);
+    }
+}
+#endif
diff --git a/legacy/src/core/SkFP.h b/legacy/src/core/SkFP.h
new file mode 100644
index 0000000..1d1507a
--- /dev/null
+++ b/legacy/src/core/SkFP.h
@@ -0,0 +1,79 @@
+
+/*
+ * 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 SkFP_DEFINED
+#define SkFP_DEFINED
+
+#include "SkMath.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+    typedef float SkFP;
+
+    #define SkScalarToFP(n)         (n)
+    #define SkFPToScalar(n)         (n)
+    #define SkIntToFP(n)            SkIntToScalar(n)
+    #define SkFPRound(x)            SkScalarRound(n)
+    #define SkFPCeil(x)             SkScalarCeil(n)
+    #define SkFPFloor(x)            SkScalarFloor(n)
+
+    #define SkFPNeg(x)              (-(x))
+    #define SkFPAbs(x)              SkScalarAbs(x)
+    #define SkFPAdd(a, b)           ((a) + (b))
+    #define SkFPSub(a, b)           ((a) - (b))
+    #define SkFPMul(a, b)           ((a) * (b))
+    #define SkFPMulInt(a, n)        ((a) * (n))
+    #define SkFPDiv(a, b)           ((a) / (b))
+    #define SkFPDivInt(a, n)        ((a) / (n))
+    #define SkFPInvert(x)           SkScalarInvert(x)
+    #define SkFPSqrt(x)             SkScalarSqrt(x)
+    #define SkFPCubeRoot(x)         sk_float_pow(x, 0.3333333f)
+
+    #define SkFPLT(a, b)            ((a) < (b))
+    #define SkFPLE(a, b)            ((a) <= (b))
+    #define SkFPGT(a, b)            ((a) > (b))
+    #define SkFPGE(a, b)            ((a) >= (b))
+
+#else   // scalar is fixed
+
+    #include "SkFloat.h"
+
+    typedef int32_t SkFP;
+
+    #define SkScalarToFP(n)         SkFloat::SetShift(n, -16)
+    #define SkFPToScalar(n)         SkFloat::GetShift(n, -16)
+    #define SkIntToFP(n)            SkFloat::SetShift(n, 0)
+    #define SkFPRound(x)            SkFloat::Round(x);
+    #define SkFPCeil(x)             SkFloat::Ceil();
+    #define SkFPFloor(x)            SkFloat::Floor();
+
+    #define SkFPNeg(x)              SkFloat::Neg(x)
+    #define SkFPAbs(x)              SkFloat::Abs(x)
+    #define SkFPAdd(a, b)           SkFloat::Add(a, b)
+    #define SkFPSub(a, b)           SkFloat::Add(a, SkFloat::Neg(b))
+    #define SkFPMul(a, b)           SkFloat::Mul(a, b)
+    #define SkFPMulInt(a, n)        SkFloat::MulInt(a, n)
+    #define SkFPDiv(a, b)           SkFloat::Div(a, b)
+    #define SkFPDivInt(a, n)        SkFloat::DivInt(a, n)
+    #define SkFPInvert(x)           SkFloat::Invert(x)
+    #define SkFPSqrt(x)             SkFloat::Sqrt(x)
+    #define SkFPCubeRoot(x)         SkFloat::CubeRoot(x)
+
+    #define SkFPLT(a, b)            (SkFloat::Cmp(a, b) < 0)
+    #define SkFPLE(a, b)            (SkFloat::Cmp(a, b) <= 0)
+    #define SkFPGT(a, b)            (SkFloat::Cmp(a, b) > 0)
+    #define SkFPGE(a, b)            (SkFloat::Cmp(a, b) >= 0)
+
+#endif
+
+#ifdef SK_DEBUG
+    void SkFP_UnitTest();
+#endif
+
+#endif
diff --git a/legacy/src/core/SkFilterProc.cpp b/legacy/src/core/SkFilterProc.cpp
new file mode 100644
index 0000000..8c24bb6
--- /dev/null
+++ b/legacy/src/core/SkFilterProc.cpp
@@ -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.
+ */
+
+
+#include "SkFilterProc.h"
+
+/*  [1-x 1-y] [x 1-y]
+    [1-x   y] [x   y]
+*/
+
+static unsigned bilerp00(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return a00; }
+static unsigned bilerp01(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a01) >> 2; }
+static unsigned bilerp02(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01) >> 1; }
+static unsigned bilerp03(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a01) >> 2; }
+
+static unsigned bilerp10(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a10) >> 2; }
+static unsigned bilerp11(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a00 + 3 * (a01 + a10) + a11) >> 4; }
+static unsigned bilerp12(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a01) + a10 + a11) >> 3; }
+static unsigned bilerp13(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a01 + 3 * (a00 + a11) + a10) >> 4; }
+
+static unsigned bilerp20(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a10) >> 1; }
+static unsigned bilerp21(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a10) + a01 + a11) >> 3; }
+static unsigned bilerp22(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01 + a10 + a11) >> 2; }
+static unsigned bilerp23(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a01 + a11) + a00 + a10) >> 3; }
+
+static unsigned bilerp30(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a10) >> 2; }
+static unsigned bilerp31(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a10 + 3 * (a00 + a11) + a01) >> 4; }
+static unsigned bilerp32(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a10 + a11) + a00 + a01) >> 3; }
+static unsigned bilerp33(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a11 + 3 * (a01 + a10) + a00) >> 4; }
+
+static const SkFilterProc gBilerpProcs[4 * 4] = {
+    bilerp00, bilerp01, bilerp02, bilerp03,
+    bilerp10, bilerp11, bilerp12, bilerp13,
+    bilerp20, bilerp21, bilerp22, bilerp23,
+    bilerp30, bilerp31, bilerp32, bilerp33
+};
+
+const SkFilterProc* SkGetBilinearFilterProcTable()
+{
+    return gBilerpProcs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define MASK            0xFF00FF
+#define LO_PAIR(x)      ((x) & MASK)
+#define HI_PAIR(x)      (((x) >> 8) & MASK)
+#define COMBINE(lo, hi) (((lo) & ~0xFF00) | (((hi) & ~0xFF00) << 8))
+
+///////////////////////////////////////////////////////////////////////////////
+
+static unsigned bilerp4_00(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    return c00;
+}
+static unsigned bilerp4_01(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    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 unsigned bilerp4_02(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_03(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_10(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2;
+    uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_11(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_12(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_13(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_20(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_21(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_22(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_23(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_30(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_31(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_32(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_33(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static const SkFilter32Proc gBilerp32Procs[4 * 4] = {
+    bilerp4_00, bilerp4_01, bilerp4_02, bilerp4_03,
+    bilerp4_10, bilerp4_11, bilerp4_12, bilerp4_13,
+    bilerp4_20, bilerp4_21, bilerp4_22, bilerp4_23,
+    bilerp4_30, bilerp4_31, bilerp4_32, bilerp4_33
+};
+
+const SkFilter32Proc* SkGetFilter32ProcTable()
+{
+    return gBilerp32Procs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint32_t bilerptr00(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    return *a00;
+}
+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 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 lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr03(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 lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2;
+    return COMBINE(lo, hi);
+}
+
+static uint32_t bilerptr10(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c10 = *a10;
+    uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2;
+    uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr11(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr12(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr13(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static uint32_t bilerptr20(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c10 = *a10;
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr21(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr22(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr23(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3;
+    return COMBINE(lo, hi);
+}
+
+static uint32_t bilerptr30(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c10 = *a10;
+    uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr31(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr32(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3;
+    return COMBINE(lo, hi);
+}
+static uint32_t bilerptr33(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 c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static const SkFilterPtrProc gBilerpPtrProcs[4 * 4] = {
+    bilerptr00, bilerptr01, bilerptr02, bilerptr03,
+    bilerptr10, bilerptr11, bilerptr12, bilerptr13,
+    bilerptr20, bilerptr21, bilerptr22, bilerptr23,
+    bilerptr30, bilerptr31, bilerptr32, bilerptr33
+};
+
+const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable()
+{
+    return gBilerpPtrProcs;
+}
diff --git a/legacy/src/core/SkFilterProc.h b/legacy/src/core/SkFilterProc.h
new file mode 100644
index 0000000..2b515e2
--- /dev/null
+++ b/legacy/src/core/SkFilterProc.h
@@ -0,0 +1,137 @@
+
+/*
+ * 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 SkFilter_DEFINED
+#define SkFilter_DEFINED
+
+#include "SkMath.h"
+#include "SkFixed.h"
+
+typedef unsigned (*SkFilterProc)(unsigned x00, unsigned x01,
+                                 unsigned x10, unsigned x11);
+
+const SkFilterProc* SkGetBilinearFilterProcTable();
+
+inline SkFilterProc SkGetBilinearFilterProc(const SkFilterProc* table,
+                                            SkFixed x, SkFixed y)
+{
+    SkASSERT(table);
+    
+    // convert to dot 2
+    x = (unsigned)(x << 16) >> 30;
+    y = (unsigned)(y << 16) >> 30;
+    return table[(y << 2) | x];
+}
+
+inline SkFilterProc SkGetBilinearFilterProc22(const SkFilterProc* table,
+                                              unsigned x, unsigned y)
+{
+    SkASSERT(table);
+    
+    // extract low 2 bits
+    x = x << 30 >> 30;
+    y = y << 30 >> 30;
+    return table[(y << 2) | x];
+}
+
+inline const SkFilterProc* SkGetBilinearFilterProc22Row(const SkFilterProc* table,
+                                                        unsigned y)
+{
+    SkASSERT(table);
+    // extract low 2 bits and shift up 2
+    return &table[y << 30 >> 28];
+}
+
+inline SkFilterProc SkGetBilinearFilterProc22RowProc(const SkFilterProc* row,
+                                                     unsigned x)
+{
+    SkASSERT(row);    
+    // extract low 2 bits
+    return row[x << 30 >> 30];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef unsigned (*SkFilter32Proc)(uint32_t x00, uint32_t x01,
+                                   uint32_t x10, uint32_t x11);
+
+const SkFilter32Proc* SkGetFilter32ProcTable();
+
+inline SkFilter32Proc SkGetFilter32Proc22(const SkFilter32Proc* table,
+                                          unsigned x, unsigned y)
+{
+    SkASSERT(table);
+    
+    // extract low 2 bits
+    x = x << 30 >> 30;
+    y = y << 30 >> 30;
+    return table[(y << 2) | x];
+}
+
+inline const SkFilter32Proc* SkGetFilter32Proc22Row(const SkFilter32Proc* table,
+                                                    unsigned y)
+{
+    SkASSERT(table);
+    // extract low 2 bits and shift up 2
+    return &table[y << 30 >> 28];
+}
+
+inline SkFilter32Proc SkGetFilter32Proc22RowProc(const SkFilter32Proc* row,
+                                                 unsigned x)
+{
+    SkASSERT(row);
+    // extract low 2 bits
+    return row[x << 30 >> 30];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Special version of SkFilterProc. This takes the address of 4 ints, and combines them a byte at a
+    time. AABBCCDD.
+*/
+typedef uint32_t (*SkFilterPtrProc)(const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
+
+const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable();
+inline SkFilterPtrProc SkGetBilinearFilterPtrProc(const SkFilterPtrProc* table, SkFixed x, SkFixed y)
+{
+    SkASSERT(table);
+
+    // convert to dot 2
+    x = (unsigned)(x << 16) >> 30;
+    y = (unsigned)(y << 16) >> 30;
+    return table[(y << 2) | x];
+}
+
+/** Given a Y value, return a subset of the proc table for that value.
+    Pass this to SkGetBilinearFilterPtrXProc with the corresponding X value to get the
+    correct proc.
+*/
+inline const SkFilterPtrProc* SkGetBilinearFilterPtrProcYTable(const SkFilterPtrProc* table, SkFixed y)
+{
+    SkASSERT(table);
+
+    y = (unsigned)(y << 16) >> 30;
+    return table + (y << 2);
+}
+
+/** Given a subtable returned by SkGetBilinearFilterPtrProcYTable(), return the proc for the
+    specified X value.
+*/
+inline SkFilterPtrProc SkGetBilinearFilterPtrXProc(const SkFilterPtrProc* table, SkFixed x)
+{
+    SkASSERT(table);
+
+    // convert to dot 2
+    x = (unsigned)(x << 16) >> 30;
+    return table[x];
+}
+
+#endif
+
+
diff --git a/legacy/src/core/SkFlate.cpp b/legacy/src/core/SkFlate.cpp
new file mode 100644
index 0000000..a5e15d1
--- /dev/null
+++ b/legacy/src/core/SkFlate.cpp
@@ -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.
+ */
+
+
+#include "SkData.h"
+#include "SkFlate.h"
+#include "SkStream.h"
+
+#ifndef SK_ZLIB_INCLUDE
+bool SkFlate::HaveFlate() { return false; }
+bool SkFlate::Deflate(SkStream*, SkWStream*) { return false; }
+bool SkFlate::Deflate(const void*, size_t, SkWStream*) { return false; }
+bool SkFlate::Deflate(const SkData*, SkWStream*) { return false; }
+bool SkFlate::Inflate(SkStream*, SkWStream*) { return false; }
+#else
+
+// static
+bool SkFlate::HaveFlate() {
+    return true;
+}
+
+namespace {
+
+#include SK_ZLIB_INCLUDE
+
+// static
+const size_t kBufferSize = 1024;
+
+bool doFlate(bool compress, SkStream* src, SkWStream* dst) {
+    uint8_t inputBuffer[kBufferSize];
+    uint8_t outputBuffer[kBufferSize];
+    z_stream flateData;
+    flateData.zalloc = NULL;
+    flateData.zfree = NULL;
+    flateData.next_in = NULL;
+    flateData.avail_in = 0;
+    flateData.next_out = outputBuffer;
+    flateData.avail_out = kBufferSize;
+    int rc;
+    if (compress)
+        rc = deflateInit(&flateData, Z_DEFAULT_COMPRESSION);
+    else
+        rc = inflateInit(&flateData);
+    if (rc != Z_OK)
+        return false;
+
+    uint8_t* input = (uint8_t*)src->getMemoryBase();
+    size_t inputLength = src->getLength();
+    if (input == NULL || inputLength == 0) {
+        input = NULL;
+        flateData.next_in = inputBuffer;
+        flateData.avail_in = 0;
+    } else {
+        flateData.next_in = input;
+        flateData.avail_in = inputLength;
+    }
+
+    rc = Z_OK;
+    while (true) {
+        if (flateData.avail_out < kBufferSize) {
+            if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) {
+                rc = Z_BUF_ERROR;
+                break;
+            }
+            flateData.next_out = outputBuffer;
+            flateData.avail_out = kBufferSize;
+        }
+        if (rc != Z_OK)
+            break;
+        if (flateData.avail_in == 0) {
+            if (input != NULL)
+                break;
+            size_t read = src->read(&inputBuffer, kBufferSize);
+            if (read == 0)
+                break;
+            flateData.next_in = inputBuffer;
+            flateData.avail_in = read;
+        }
+        if (compress)
+            rc = deflate(&flateData, Z_NO_FLUSH);
+        else
+            rc = inflate(&flateData, Z_NO_FLUSH);
+    }
+    while (rc == Z_OK) {
+        if (compress)
+            rc = deflate(&flateData, Z_FINISH);
+        else
+            rc = inflate(&flateData, Z_FINISH);
+        if (flateData.avail_out < kBufferSize) {
+            if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out))
+                return false;
+            flateData.next_out = outputBuffer;
+            flateData.avail_out = kBufferSize;
+        }
+    }
+
+    if (compress)
+        deflateEnd(&flateData);
+    else
+        inflateEnd(&flateData);
+    if (rc == Z_STREAM_END)
+        return true;
+    return false;
+}
+
+}
+
+// static
+bool SkFlate::Deflate(SkStream* src, SkWStream* dst) {
+    return doFlate(true, src, dst);
+}
+
+bool SkFlate::Deflate(const void* ptr, size_t len, SkWStream* dst) {
+    SkMemoryStream stream(ptr, len);
+    return doFlate(true, &stream, dst);
+}
+
+bool SkFlate::Deflate(const SkData* data, SkWStream* dst) {
+    if (data) {
+        SkMemoryStream stream(data->data(), data->size());
+        return doFlate(true, &stream, dst);
+    }
+    return false;
+}
+
+// static
+bool SkFlate::Inflate(SkStream* src, SkWStream* dst) {
+    return doFlate(false, src, dst);
+}
+
+#endif
+
diff --git a/legacy/src/core/SkFlattenable.cpp b/legacy/src/core/SkFlattenable.cpp
new file mode 100644
index 0000000..131cf4f
--- /dev/null
+++ b/legacy/src/core/SkFlattenable.cpp
@@ -0,0 +1,429 @@
+
+/*
+ * 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 "SkFlattenable.h"
+#include "SkTypeface.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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFlattenable::flatten(SkFlattenableWriteBuffer&)
+{
+    /*  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()
+        in their code.
+    */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer() {
+    fRCArray = NULL;
+    fRCCount = 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];
+    }
+}
+
+SkRefCnt* SkFlattenableReadBuffer::readRefCnt() {
+    uint32_t index = this->readU32();
+    if (0 == index || index > (unsigned)fRCCount) {
+        return NULL;
+    } else {
+        SkASSERT(fRCArray);
+        return fRCArray[index - 1];
+    }
+}
+
+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;
+    }
+
+    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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRefCntSet::~SkRefCntSet() {
+    // call this now, while our decPtr() is sill in scope
+    this->reset();
+}
+
+void SkRefCntSet::incPtr(void* ptr) {
+    ((SkRefCnt*)ptr)->ref();
+}
+
+void SkRefCntSet::decPtr(void* ptr) {
+    ((SkRefCnt*)ptr)->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define MAX_PAIR_COUNT  64
+
+struct Pair {
+    const char*             fName;
+    SkFlattenable::Factory  fFactory;
+};
+
+static int gCount;
+static Pair gPairs[MAX_PAIR_COUNT];
+
+void SkFlattenable::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
+
+SkFlattenable::Factory SkFlattenable::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* SkFlattenable::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;
+        }
+    }
+    return NULL;
+}
+
+bool SkFlattenable::toDumpString(SkString* str) const {
+    return false;
+}
diff --git a/legacy/src/core/SkFloat.cpp b/legacy/src/core/SkFloat.cpp
new file mode 100644
index 0000000..ffa5d9a
--- /dev/null
+++ b/legacy/src/core/SkFloat.cpp
@@ -0,0 +1,397 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkFloat.h"
+#include "SkMath.h"
+
+#define EXP_BIAS    (127+23)
+
+static int get_unsigned_exp(uint32_t packed)
+{
+    return (packed << 1 >> 24);
+}
+
+static unsigned get_unsigned_value(uint32_t packed)
+{
+    return (packed << 9 >> 9) | (1 << 23);
+}
+
+static int get_signed_value(int32_t packed)
+{
+    return SkApplySign(get_unsigned_value(packed), SkExtractSign(packed));
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+int SkFloat::GetShift(int32_t packed, int shift)
+{
+    if (packed == 0)
+        return 0;
+
+    int exp = get_unsigned_exp(packed) - EXP_BIAS - shift;
+    int value = get_unsigned_value(packed);
+
+    if (exp >= 0)
+    {
+        if (exp > 8)    // overflow
+            value = SK_MaxS32;
+        else
+            value <<= exp;
+    }
+    else
+    {
+        exp = -exp;
+        if (exp > 23)   // underflow
+            value = 0;
+        else
+            value >>= exp;
+    }
+    return SkApplySign(value, SkExtractSign(packed));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+int32_t SkFloat::SetShift(int value, int shift)
+{
+    if (value == 0)
+        return 0;
+
+    // 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);
+        SkASSERT(bias > 0 && bias < 8);
+        value >>= bias;
+        shift += bias;
+    }
+    else
+    {
+        int zeros = SkCLZ(value << 8);
+        SkASSERT(zeros >= 0 && zeros <= 23);
+        value <<= zeros;
+        shift -= zeros;
+    }
+    // now value is left-aligned to 24 bits
+    SkASSERT((value >> 23) == 1);
+
+    shift += EXP_BIAS;
+    if (shift < 0)  // underflow
+        return 0;
+    else
+    {
+        if (shift > 255)    // overflow
+        {
+            shift = 255;
+            value = 0x00FFFFFF;
+        }
+        int32_t packed = sign << 31;        // set the sign-bit
+        packed |= shift << 23;          // store the packed exponent
+        packed |= ((unsigned)(value << 9) >> 9);    // clear 24th bit of value (its implied)
+
+#ifdef SK_DEBUG
+        {
+            int n;
+
+            n = SkExtractSign(packed);
+            SkASSERT(n == sign);
+            n = get_unsigned_exp(packed);
+            SkASSERT(n == shift);
+            n = get_unsigned_value(packed);
+            SkASSERT(n == value);
+        }
+#endif
+        return packed;
+    }
+}
+
+int32_t SkFloat::Neg(int32_t packed)
+{
+    if (packed)
+        packed = packed ^ (1 << 31);
+    return packed;
+}
+
+int32_t SkFloat::Add(int32_t packed_a, int32_t packed_b)
+{
+    if (packed_a == 0)
+        return packed_b;
+    if (packed_b == 0)
+        return packed_a;
+
+    int exp_a = get_unsigned_exp(packed_a);
+    int exp_b = get_unsigned_exp(packed_b);
+    int exp_diff = exp_a - exp_b;
+
+    int shift_a = 0, shift_b = 0;
+    int exp;
+
+    if (exp_diff >= 0)
+    {
+        if (exp_diff > 24)  // B is too small to contribute
+            return packed_a;
+        shift_b = exp_diff;
+        exp = exp_a;
+    }
+    else
+    {
+        exp_diff = -exp_diff;
+        if (exp_diff > 24)  // A is too small to contribute
+            return packed_b;
+        shift_a = exp_diff;
+        exp = exp_b;
+    }
+
+    int value_a = get_signed_value(packed_a) >> shift_a;
+    int value_b = get_signed_value(packed_b) >> shift_b;
+
+    return SkFloat::SetShift(value_a + value_b, exp - EXP_BIAS);
+}
+
+#include "Sk64.h"
+
+static inline int32_t mul24(int32_t a, int32_t b)
+{
+    Sk64 tmp;
+
+    tmp.setMul(a, b);
+    tmp.roundRight(24);
+    return tmp.get32();
+}
+
+int32_t SkFloat::Mul(int32_t packed_a, int32_t packed_b)
+{
+    if (packed_a == 0 || packed_b == 0)
+        return 0;
+
+    int exp_a = get_unsigned_exp(packed_a);
+    int exp_b = get_unsigned_exp(packed_b);
+
+    int value_a = get_signed_value(packed_a);
+    int value_b = get_signed_value(packed_b);
+
+    return SkFloat::SetShift(mul24(value_a, value_b), exp_a + exp_b - 2*EXP_BIAS + 24);
+}
+
+int32_t SkFloat::MulInt(int32_t packed, int n)
+{
+    return Mul(packed, SetShift(n, 0));
+}
+
+int32_t SkFloat::Div(int32_t packed_n, int32_t packed_d)
+{
+    SkASSERT(packed_d != 0);
+
+    if (packed_n == 0)
+        return 0;
+
+    int exp_n = get_unsigned_exp(packed_n);
+    int exp_d = get_unsigned_exp(packed_d);
+
+    int value_n = get_signed_value(packed_n);
+    int value_d = get_signed_value(packed_d);
+
+    return SkFloat::SetShift(SkDivBits(value_n, value_d, 24), exp_n - exp_d - 24);
+}
+
+int32_t SkFloat::DivInt(int32_t packed, int n)
+{
+    return Div(packed, SetShift(n, 0));
+}
+
+int32_t SkFloat::Invert(int32_t packed)
+{
+    return Div(packed, SetShift(1, 0));
+}
+
+int32_t SkFloat::Sqrt(int32_t packed)
+{
+    if (packed < 0)
+    {
+        SkDEBUGFAIL("can't sqrt a negative number");
+        return 0;
+    }
+
+    int exp = get_unsigned_exp(packed);
+    int value = get_unsigned_value(packed);
+
+    int nexp = exp - EXP_BIAS;
+    int root = SkSqrtBits(value << (nexp & 1), 26);
+    nexp >>= 1;
+    return SkFloat::SetShift(root, nexp - 11);
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : unreachable code
+#pragma warning ( push )
+#pragma warning ( disable : 4702 )
+#endif
+
+int32_t SkFloat::CubeRoot(int32_t packed)
+{
+    sk_throw();
+    return 0;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+static inline int32_t clear_high_bit(int32_t n)
+{
+    return ((uint32_t)(n << 1)) >> 1;
+}
+
+static inline int int_sign(int32_t a, int32_t b)
+{
+    return a > b ? 1 : (a < b ? -1 : 0);
+}
+
+int SkFloat::Cmp(int32_t packed_a, int32_t packed_b)
+{
+    packed_a = SkApplySign(clear_high_bit(packed_a), SkExtractSign(packed_a));
+    packed_b = SkApplySign(clear_high_bit(packed_b), SkExtractSign(packed_b));
+
+    return int_sign(packed_a, packed_b);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+#ifdef SK_CAN_USE_FLOAT
+    #include "SkFloatingPoint.h"
+#endif
+
+void SkFloat::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkFloat a, b, c, d;
+    int     n;
+
+    a.setZero();
+    n = a.getInt();
+    SkASSERT(n == 0);
+
+    b.setInt(5);
+    n = b.getInt();
+    SkASSERT(n == 5);
+
+    c.setInt(-3);
+    n = c.getInt();
+    SkASSERT(n == -3);
+
+    d.setAdd(c, b);
+    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++)
+    {
+        float fa, fb;
+        int aa = rand.nextS() >> 14;
+        int bb = rand.nextS() >> 14;
+        a.setInt(aa);
+        b.setInt(bb);
+        SkASSERT(a.getInt() == aa);
+        SkASSERT(b.getInt() == bb);
+
+        c.setAdd(a, b);
+        int cc = c.getInt();
+        SkASSERT(cc == aa + bb);
+
+        c.setSub(a, b);
+        cc = c.getInt();
+        SkASSERT(cc == aa - bb);
+
+        aa >>= 5;
+        bb >>= 5;
+        a.setInt(aa);
+        b.setInt(bb);
+        c.setMul(a, b);
+        cc = c.getInt();
+        SkASSERT(cc == aa * bb);
+        /////////////////////////////////////
+
+        aa = rand.nextS() >> 11;
+        a.setFixed(aa);
+        cc = a.getFixed();
+        SkASSERT(aa == cc);
+
+        bb = rand.nextS() >> 11;
+        b.setFixed(bb);
+        cc = b.getFixed();
+        SkASSERT(bb == cc);
+
+        cc = SkFixedMul(aa, bb);
+        c.setMul(a, b);
+        SkFixed dd = c.getFixed();
+        int diff = cc - dd;
+        SkASSERT(SkAbs32(diff) <= 1);
+
+        fa = (float)aa / 65536.0f;
+        fb = (float)bb / 65536.0f;
+        a.assertEquals(fa);
+        b.assertEquals(fb);
+        fa = a.getFloat();
+        fb = b.getFloat();
+
+        c.assertEquals(fa * fb, 1);
+
+        c.setDiv(a, b);
+        cc = SkFixedDiv(aa, bb);
+        dd = c.getFixed();
+        diff = cc - dd;
+        SkASSERT(SkAbs32(diff) <= 3);
+
+        c.assertEquals(fa / fb, 1);
+
+        SkASSERT((aa == bb) == (a == b));
+        SkASSERT((aa != bb) == (a != b));
+        SkASSERT((aa < bb) == (a < b));
+        SkASSERT((aa <= bb) == (a <= b));
+        SkASSERT((aa > bb) == (a > b));
+        SkASSERT((aa >= bb) == (a >= b));
+
+        if (aa < 0)
+        {
+            aa = -aa;
+            fa = -fa;
+        }
+        a.setFixed(aa);
+        c.setSqrt(a);
+        cc = SkFixedSqrt(aa);
+        dd = c.getFixed();
+        SkASSERT(dd == cc);
+
+        c.assertEquals(sk_float_sqrt(fa), 2);
+
+        // cuberoot
+#if 0
+        a.setInt(1);
+        a.cubeRoot();
+        a.assertEquals(1.0f, 0);
+        a.setInt(8);
+        a.cubeRoot();
+        a.assertEquals(2.0f, 0);
+        a.setInt(27);
+        a.cubeRoot();
+        a.assertEquals(3.0f, 0);
+#endif
+    }
+#endif
+#endif
+}
+
+#endif
diff --git a/legacy/src/core/SkFloat.h b/legacy/src/core/SkFloat.h
new file mode 100644
index 0000000..fe41c27
--- /dev/null
+++ b/legacy/src/core/SkFloat.h
@@ -0,0 +1,109 @@
+
+/*
+ * 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 SkFloat_DEFINED
+#define SkFloat_DEFINED
+
+#include "SkFixed.h"
+
+class SkFloat {
+public:
+    SkFloat() {}
+
+    void    setZero() { fPacked = 0; }
+//  void    setShift(int value, int shift) { fPacked = SetShift(value, shift); }
+    void    setInt(int value) { fPacked = SetShift(value, 0); }
+    void    setFixed(SkFixed value) { fPacked = SetShift(value, -16); }
+    void    setFract(SkFract value) { fPacked = SetShift(value, -30); }
+
+//  int     getShift(int shift) const { return GetShift(fPacked, shift); }
+    int     getInt() const { return GetShift(fPacked, 0); }
+    SkFixed getFixed() const { return GetShift(fPacked, -16); }
+    SkFract getFract() const { return GetShift(fPacked, -30); }
+
+    void    abs() { fPacked = Abs(fPacked); }
+    void    negate() { fPacked = Neg(fPacked); }
+
+    void    shiftLeft(int bits) { fPacked = Shift(fPacked, bits); }
+    void    setShiftLeft(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, bits); }
+
+    void    shiftRight(int bits) { fPacked = Shift(fPacked, -bits); }
+    void    setShiftRight(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, -bits); }
+
+    void    add(const SkFloat& a) { fPacked = Add(fPacked, a.fPacked); }
+    void    setAdd(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, b.fPacked); }
+
+    void    sub(const SkFloat& a) { fPacked = Add(fPacked, Neg(a.fPacked)); }
+    void    setSub(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, Neg(b.fPacked)); }
+
+    void    mul(const SkFloat& a) { fPacked = Mul(fPacked, a.fPacked); }
+    void    setMul(const SkFloat& a, const SkFloat& b) { fPacked = Mul(a.fPacked, b.fPacked); }
+
+    void    div(const SkFloat& a) { fPacked = Div(fPacked, a.fPacked); }
+    void    setDiv(const SkFloat& a, const SkFloat& b) { fPacked = Div(a.fPacked, b.fPacked); }
+
+    void    sqrt() { fPacked = Sqrt(fPacked); }
+    void    setSqrt(const SkFloat& a) { fPacked = Sqrt(a.fPacked); }
+    void    cubeRoot() { fPacked = CubeRoot(fPacked); }
+    void    setCubeRoot(const SkFloat& a) { fPacked = CubeRoot(a.fPacked); }
+
+    friend bool operator==(const SkFloat& a, const SkFloat& b) { return a.fPacked == b.fPacked; }
+    friend bool operator!=(const SkFloat& a, const SkFloat& b) { return a.fPacked != b.fPacked; }
+    friend bool operator<(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) < 0; }
+    friend bool operator<=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) <= 0; }
+    friend bool operator>(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) > 0; }
+    friend bool operator>=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) >= 0; }
+
+#ifdef SK_DEBUG
+    static void UnitTest();
+
+    void assertEquals(float f, int tolerance = 0)
+    {
+        union {
+            float   fFloat;
+            int32_t fPacked;
+        } tmp;
+        
+        tmp.fFloat = f;
+        int d = tmp.fPacked - fPacked;
+        SkASSERT(SkAbs32(d) <= tolerance);
+    }
+    float getFloat() const
+    {
+        union {
+            float   fFloat;
+            int32_t fPacked;
+        } tmp;
+        
+        tmp.fPacked = fPacked;
+        return tmp.fFloat;
+    }
+#endif
+
+private:
+    int32_t fPacked;
+
+public:
+    static int GetShift(int32_t packed, int shift);
+    static int32_t SetShift(int value, int shift);
+    static int32_t Neg(int32_t);
+    static int32_t Abs(int32_t packed) { return (uint32_t)(packed << 1) >> 1; }
+    static int32_t Shift(int32_t, int bits);
+    static int32_t Add(int32_t, int32_t);
+    static int32_t Mul(int32_t, int32_t);
+    static int32_t MulInt(int32_t, int);
+    static int32_t Div(int32_t, int32_t);
+    static int32_t DivInt(int32_t, int);
+    static int32_t Invert(int32_t);
+    static int32_t Sqrt(int32_t);
+    static int32_t CubeRoot(int32_t);
+    static int Cmp(int32_t, int32_t);
+};
+
+#endif
diff --git a/legacy/src/core/SkFloatBits.cpp b/legacy/src/core/SkFloatBits.cpp
new file mode 100644
index 0000000..fc46005
--- /dev/null
+++ b/legacy/src/core/SkFloatBits.cpp
@@ -0,0 +1,210 @@
+
+/*
+ * 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"
+
+/******************************************************************************
+    SkFloatBits_toInt[Floor, Round, Ceil] are identical except for what they
+    do right before they return ... >> exp;
+    Floor - adds nothing
+    Round - adds 1 << (exp - 1)
+    Ceil - adds (1 << exp) - 1
+
+    Floor and Cast are very similar, but Cast applies its sign after all other
+    computations on value. Also, Cast does not need to check for negative zero,
+    as that value (0x80000000) "does the right thing" for Ceil. Note that it
+    doesn't for Floor/Round/Ceil, hence the explicit check.
+******************************************************************************/
+
+#define EXP_BIAS            (127+23)
+#define MATISSA_MAGIC_BIG   (1 << 23)
+
+static inline int unpack_exp(uint32_t packed) {
+    return (packed << 1 >> 24);
+}
+
+#if 0
+// the ARM compiler generates an extra BIC, so I use the dirty version instead
+static inline int unpack_matissa(uint32_t packed) {
+    // we could mask with 0x7FFFFF, but that is harder for ARM to encode
+    return (packed & ~0xFF000000) | MATISSA_MAGIC_BIG;
+}
+#endif
+
+// returns the low 24-bits, so we need to OR in the magic_bit afterwards
+static inline int unpack_matissa_dirty(uint32_t packed) {
+    return packed & ~0xFF000000;
+}
+
+// same as (int)float
+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;
+        } else {
+            value <<= exp;
+        }
+    } else {
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        value >>= exp;
+    }
+    return SkApplySign(value, SkExtractSign(packed));
+}
+
+// same as (int)floor(float)
+int32_t SkFloatBits_toIntFloor(int32_t packed) {
+    // curse you negative 0
+    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;
+        } else {
+            value <<= exp;
+        }
+        // apply the sign after we check for overflow
+        return SkApplySign(value, SkExtractSign(packed));
+    } else {
+        // apply the sign before we right-shift
+        value = SkApplySign(value, SkExtractSign(packed));
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        // int add = 0;
+        return value >> exp;
+    }
+}
+
+// same as (int)floor(float + 0.5)
+int32_t SkFloatBits_toIntRound(int32_t packed) {
+    // curse you negative 0
+    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;
+        } else {
+            value <<= exp;
+        }
+        // apply the sign after we check for overflow
+        return SkApplySign(value, SkExtractSign(packed));
+    } else {
+        // apply the sign before we right-shift
+        value = SkApplySign(value, SkExtractSign(packed));
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        int add = 1 << (exp - 1);
+        return (value + add) >> exp;
+    }
+}
+
+// same as (int)ceil(float)
+int32_t SkFloatBits_toIntCeil(int32_t packed) {
+    // curse you negative 0
+    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;
+        } else {
+            value <<= exp;
+        }
+        // apply the sign after we check for overflow
+        return SkApplySign(value, SkExtractSign(packed));
+    } else {
+        // apply the sign before we right-shift
+        value = SkApplySign(value, SkExtractSign(packed));
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        int add = (1 << exp) - 1;
+        return (value + add) >> exp;
+    }
+}
+
+#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);
+        SkASSERT(bias > 0 && bias < 8);
+        value >>= bias; // need to round?
+        shift += bias;
+    } else {
+        int zeros = SkCLZ(value << 8);
+        SkASSERT(zeros >= 0 && zeros <= 23);
+        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;
+}
+
+float SkIntToFloatCast_NoOverflowCheck(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);
+    
+    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/legacy/src/core/SkFontHost.cpp b/legacy/src/core/SkFontHost.cpp
new file mode 100644
index 0000000..c1836ac
--- /dev/null
+++ b/legacy/src/core/SkFontHost.cpp
@@ -0,0 +1,37 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkFontHost.h"
+
+static SkFontHost::LCDOrientation gLCDOrientation = SkFontHost::kHorizontal_LCDOrientation;
+static SkFontHost::LCDOrder gLCDOrder = SkFontHost::kRGB_LCDOrder;
+
+// static
+SkFontHost::LCDOrientation SkFontHost::GetSubpixelOrientation()
+{
+    return gLCDOrientation;
+}
+
+// static
+void SkFontHost::SetSubpixelOrientation(LCDOrientation orientation)
+{
+    gLCDOrientation = orientation;
+}
+
+// static
+SkFontHost::LCDOrder SkFontHost::GetSubpixelOrder()
+{
+    return gLCDOrder;
+}
+
+// static
+void SkFontHost::SetSubpixelOrder(LCDOrder order)
+{
+    gLCDOrder = order;
+}
diff --git a/legacy/src/core/SkGeometry.cpp b/legacy/src/core/SkGeometry.cpp
new file mode 100644
index 0000000..de86827
--- /dev/null
+++ b/legacy/src/core/SkGeometry.cpp
@@ -0,0 +1,1363 @@
+
+/*
+ * 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 "SkGeometry.h"
+#include "Sk64.h"
+#include "SkMatrix.h"
+
+bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2], bool* ambiguous) {
+    if (ambiguous) {
+        *ambiguous = false;
+    }
+    // Determine quick discards.
+    // Consider query line going exactly through point 0 to not
+    // intersect, for symmetry with SkXRayCrossesMonotonicCubic.
+    if (pt.fY == pts[0].fY) {
+        if (ambiguous) {
+            *ambiguous = true;
+        }
+        return false;
+    }
+    if (pt.fY < pts[0].fY && pt.fY < pts[1].fY)
+        return false;
+    if (pt.fY > pts[0].fY && pt.fY > pts[1].fY)
+        return false;
+    if (pt.fX > pts[0].fX && pt.fX > pts[1].fX)
+        return false;
+    // Determine degenerate cases
+    if (SkScalarNearlyZero(pts[0].fY - pts[1].fY))
+        return false;
+    if (SkScalarNearlyZero(pts[0].fX - pts[1].fX)) {
+        // We've already determined the query point lies within the
+        // vertical range of the line segment.
+        if (pt.fX <= pts[0].fX) {
+            if (ambiguous) {
+                *ambiguous = (pt.fY == pts[1].fY);
+            }
+            return true;
+        }
+        return false;
+    }
+    // Ambiguity check
+    if (pt.fY == pts[1].fY) {
+        if (pt.fX <= pts[1].fX) {
+            if (ambiguous) {
+                *ambiguous = true;
+            }
+            return true;
+        }
+        return false;
+    }
+    // Full line segment evaluation
+    SkScalar delta_y = pts[1].fY - pts[0].fY;
+    SkScalar delta_x = pts[1].fX - pts[0].fX;
+    SkScalar slope = SkScalarDiv(delta_y, delta_x);
+    SkScalar b = pts[0].fY - SkScalarMul(slope, pts[0].fX);
+    // Solve for x coordinate at y = pt.fY
+    SkScalar x = SkScalarDiv(pt.fY - b, slope);
+    return pt.fX <= x;
+}
+
+/** If defined, this makes eval_quad and eval_cubic do more setup (sometimes
+    involving integer multiplies by 2 or 3, but fewer calls to SkScalarMul.
+    May also introduce overflow of fixed when we compute our setup.
+*/
+#ifdef SK_SCALAR_IS_FIXED
+    #define DIRECT_EVAL_OF_POLYNOMIALS
+#endif
+
+////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+    static int is_not_monotonic(int a, int b, int c, int d)
+    {
+        return (((a - b) | (b - c) | (c - d)) & ((b - a) | (c - b) | (d - c))) >> 31;
+    }
+
+    static int is_not_monotonic(int a, int b, int c)
+    {
+        return (((a - b) | (b - c)) & ((b - a) | (c - b))) >> 31;
+    }
+#else
+    static int is_not_monotonic(float a, float b, float c)
+    {
+        float ab = a - b;
+        float bc = b - c;
+        if (ab < 0)
+            bc = -bc;
+        return ab == 0 || bc < 0;
+    }
+#endif
+
+////////////////////////////////////////////////////////////////////////
+
+static bool is_unit_interval(SkScalar x)
+{
+    return x > 0 && x < SK_Scalar1;
+}
+
+static int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio)
+{
+    SkASSERT(ratio);
+
+    if (numer < 0)
+    {
+        numer = -numer;
+        denom = -denom;
+    }
+
+    if (denom == 0 || numer == 0 || numer >= denom)
+        return 0;
+
+    SkScalar r = SkScalarDiv(numer, denom);
+    if (SkScalarIsNaN(r)) {
+        return 0;
+    }
+    SkASSERT(r >= 0 && r < SK_Scalar1);
+    if (r == 0) // catch underflow if numer <<<< denom
+        return 0;
+    *ratio = r;
+    return 1;
+}
+
+/** From Numerical Recipes in C.
+
+    Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C])
+    x1 = Q / A
+    x2 = C / Q
+*/
+int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
+{
+    SkASSERT(roots);
+
+    if (A == 0)
+        return valid_unit_divide(-C, B, roots);
+
+    SkScalar* r = roots;
+
+#ifdef SK_SCALAR_IS_FLOAT
+    float R = B*B - 4*A*C;
+    if (R < 0 || SkScalarIsNaN(R)) {  // complex roots
+        return 0;
+    }
+    R = sk_float_sqrt(R);
+#else
+    Sk64    RR, tmp;
+
+    RR.setMul(B,B);
+    tmp.setMul(A,C);
+    tmp.shiftLeft(2);
+    RR.sub(tmp);
+    if (RR.isNeg())
+        return 0;
+    SkFixed R = RR.getSqrt();
+#endif
+
+    SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
+    r += valid_unit_divide(Q, A, r);
+    r += valid_unit_divide(C, Q, r);
+    if (r - roots == 2)
+    {
+        if (roots[0] > roots[1])
+            SkTSwap<SkScalar>(roots[0], roots[1]);
+        else if (roots[0] == roots[1])  // nearly-equal?
+            r -= 1; // skip the double root
+    }
+    return (int)(r - roots);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+/** Trim A/B/C down so that they are all <= 32bits
+    and then call SkFindUnitQuadRoots()
+*/
+static int Sk64FindFixedQuadRoots(const Sk64& A, const Sk64& B, const Sk64& C, SkFixed roots[2])
+{
+    int na = A.shiftToMake32();
+    int nb = B.shiftToMake32();
+    int nc = C.shiftToMake32();
+
+    int shift = SkMax32(na, SkMax32(nb, nc));
+    SkASSERT(shift >= 0);
+
+    return SkFindUnitQuadRoots(A.getShiftRight(shift), B.getShiftRight(shift), C.getShiftRight(shift), roots);
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+static SkScalar eval_quad(const SkScalar src[], SkScalar t)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+#ifdef DIRECT_EVAL_OF_POLYNOMIALS
+    SkScalar    C = src[0];
+    SkScalar    A = src[4] - 2 * src[2] + C;
+    SkScalar    B = 2 * (src[2] - C);
+    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); 
+    return SkScalarInterp(ab, bc, t);
+#endif
+}
+
+static SkScalar eval_quad_derivative(const SkScalar src[], SkScalar t)
+{
+    SkScalar A = src[4] - 2 * src[2] + src[0];
+    SkScalar B = src[2] - src[0];
+
+    return 2 * SkScalarMulAdd(A, t, B);
+}
+
+static SkScalar eval_quad_derivative_at_half(const SkScalar src[])
+{
+    SkScalar A = src[4] - 2 * src[2] + src[0];
+    SkScalar B = src[2] - src[0];
+    return A + 2 * B;
+}
+
+void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    if (pt)
+        pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t));
+    if (tangent)
+        tangent->set(eval_quad_derivative(&src[0].fX, t),
+                     eval_quad_derivative(&src[0].fY, t));
+}
+
+void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent)
+{
+    SkASSERT(src);
+
+    if (pt)
+    {
+        SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
+        SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
+        SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
+        SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
+        pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
+    }
+    if (tangent)
+        tangent->set(eval_quad_derivative_at_half(&src[0].fX),
+                     eval_quad_derivative_at_half(&src[0].fY));
+}
+
+static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
+{
+    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
+
+    dst[0] = src[0];
+    dst[2] = ab;
+    dst[4] = SkScalarInterp(ab, bc, t);
+    dst[6] = bc;
+    dst[8] = src[4];
+}
+
+void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t)
+{
+    SkASSERT(t > 0 && t < SK_Scalar1);
+
+    interp_quad_coords(&src[0].fX, &dst[0].fX, t);
+    interp_quad_coords(&src[0].fY, &dst[0].fY, t);
+}
+
+void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5])
+{
+    SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
+    SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
+    SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
+    SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
+
+    dst[0] = src[0];
+    dst[1].set(x01, y01);
+    dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
+    dst[3].set(x12, y12);
+    dst[4] = src[2];
+}
+
+/** Quad'(t) = At + B, where
+    A = 2(a - 2b + c)
+    B = 2(b - a)
+    Solve for t, only if it fits between 0 < t < 1
+*/
+int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1])
+{
+    /*  At + B == 0
+        t = -B / A
+    */
+#ifdef SK_SCALAR_IS_FIXED
+    return is_not_monotonic(a, b, c) && valid_unit_divide(a - b, a - b - b + c, tValue);
+#else
+    return valid_unit_divide(a - b, a - b - b + c, tValue);
+#endif
+}
+
+static inline void flatten_double_quad_extrema(SkScalar coords[14])
+{
+    coords[2] = coords[6] = coords[4];
+}
+
+/*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
+ stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
+ */
+int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5])
+{
+    SkASSERT(src);
+    SkASSERT(dst);
+    
+#if 0
+    static bool once = true;
+    if (once)
+    {
+        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;
+        if (valid_unit_divide(a - b, a - b - b + c, &tValue))
+        {
+            SkChopQuadAt(src, dst, tValue);
+            flatten_double_quad_extrema(&dst[0].fY);
+            return 1;
+        }
+        // if we get here, we need to force dst to be monotonic, even though
+        // we couldn't compute a unit_divide value (probably underflow).
+        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
+    }
+    dst[0].set(src[0].fX, a);
+    dst[1].set(src[1].fX, b);
+    dst[2].set(src[2].fX, c);
+    return 0;
+}
+
+/*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
+    stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
+ */
+int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5])
+{
+    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)) {
+            SkChopQuadAt(src, dst, tValue);
+            flatten_double_quad_extrema(&dst[0].fX);
+            return 1;
+        }
+        // if we get here, we need to force dst to be monotonic, even though
+        // we couldn't compute a unit_divide value (probably underflow).
+        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
+    }
+    dst[0].set(a, src[0].fY);
+    dst[1].set(b, src[1].fY);
+    dst[2].set(c, src[2].fY);
+    return 0;
+}
+
+//  F(t)    = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2
+//  F'(t)   = 2 (b - a) + 2 (a - 2b + c) t
+//  F''(t)  = 2 (a - 2b + c)
+//
+//  A = 2 (b - a)
+//  B = 2 (a - 2b + c)
+//
+//  Maximum curvature for a quadratic means solving
+//  Fx' Fx'' + Fy' Fy'' = 0
+//
+//  t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2)
+//
+int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5])
+{
+    SkScalar    Ax = src[1].fX - src[0].fX;
+    SkScalar    Ay = src[1].fY - src[0].fY;
+    SkScalar    Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
+    SkScalar    By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
+    SkScalar    t = 0;  // 0 means don't chop
+
+#ifdef SK_SCALAR_IS_FLOAT
+    (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t);
+#else
+    // !!! should I use SkFloat here? seems like it
+    Sk64    numer, denom, tmp;
+
+    numer.setMul(Ax, -Bx);
+    tmp.setMul(Ay, -By);
+    numer.add(tmp);
+
+    if (numer.isPos())  // do nothing if numer <= 0
+    {
+        denom.setMul(Bx, Bx);
+        tmp.setMul(By, By);
+        denom.add(tmp);
+        SkASSERT(!denom.isNeg());
+        if (numer < denom)
+        {
+            t = numer.getFixedDiv(denom);
+            SkASSERT(t >= 0 && t <= SK_Fixed1);     // assert that we're numerically stable (ha!)
+            if ((unsigned)t >= SK_Fixed1)           // runtime check for numerical stability
+                t = 0;  // ignore the chop
+        }
+    }
+#endif
+
+    if (t == 0)
+    {
+        memcpy(dst, src, 3 * sizeof(SkPoint));
+        return 1;
+    }
+    else
+    {
+        SkChopQuadAt(src, dst, t);
+        return 2;
+    }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SK_ScalarTwoThirds  (0.666666666f)
+#else
+    #define SK_ScalarTwoThirds  ((SkFixed)(43691))
+#endif
+
+void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]) {
+    const SkScalar scale = SK_ScalarTwoThirds;
+    dst[0] = src[0];
+    dst[1].set(src[0].fX + SkScalarMul(src[1].fX - src[0].fX, scale),
+               src[0].fY + SkScalarMul(src[1].fY - src[0].fY, scale));
+    dst[2].set(src[2].fX + SkScalarMul(src[1].fX - src[2].fX, scale),
+               src[2].fY + SkScalarMul(src[1].fY - src[2].fY, scale));
+    dst[3] = src[2];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
+////////////////////////////////////////////////////////////////////////////////////////
+
+static void get_cubic_coeff(const SkScalar pt[], SkScalar coeff[4])
+{
+    coeff[0] = pt[6] + 3*(pt[2] - pt[4]) - pt[0];
+    coeff[1] = 3*(pt[4] - pt[2] - pt[2] + pt[0]);
+    coeff[2] = 3*(pt[2] - pt[0]);
+    coeff[3] = pt[0];
+}
+
+void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4])
+{
+    SkASSERT(pts);
+
+    if (cx)
+        get_cubic_coeff(&pts[0].fX, cx);
+    if (cy)
+        get_cubic_coeff(&pts[0].fY, cy);
+}
+
+static SkScalar eval_cubic(const SkScalar src[], SkScalar t)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    if (t == 0)
+        return src[0];
+
+#ifdef DIRECT_EVAL_OF_POLYNOMIALS
+    SkScalar D = src[0];
+    SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
+    SkScalar B = 3*(src[4] - src[2] - src[2] + D);
+    SkScalar C = 3*(src[2] - D);
+
+    return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
+#else
+    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
+    SkScalar    cd = SkScalarInterp(src[4], src[6], t);
+    SkScalar    abc = SkScalarInterp(ab, bc, t);
+    SkScalar    bcd = SkScalarInterp(bc, cd, t);
+    return SkScalarInterp(abc, bcd, t);
+#endif
+}
+
+/** return At^2 + Bt + C
+*/
+static SkScalar eval_quadratic(SkScalar A, SkScalar B, SkScalar C, SkScalar t)
+{
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
+}
+
+static SkScalar eval_cubic_derivative(const SkScalar src[], SkScalar t)
+{
+    SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
+    SkScalar B = 2*(src[4] - 2 * src[2] + src[0]);
+    SkScalar C = src[2] - src[0];
+
+    return eval_quadratic(A, B, C, t);
+}
+
+static SkScalar eval_cubic_2ndDerivative(const SkScalar src[], SkScalar t)
+{
+    SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
+    SkScalar B = src[4] - 2 * src[2] + src[0];
+
+    return SkScalarMulAdd(A, t, B);
+}
+
+void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, SkVector* tangent, SkVector* curvature)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    if (loc)
+        loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t));
+    if (tangent)
+        tangent->set(eval_cubic_derivative(&src[0].fX, t),
+                     eval_cubic_derivative(&src[0].fY, t));
+    if (curvature)
+        curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t),
+                       eval_cubic_2ndDerivative(&src[0].fY, t));
+}
+
+/** Cubic'(t) = At^2 + Bt + C, where
+    A = 3(-a + 3(b - c) + d)
+    B = 6(a - 2b + c)
+    C = 3(b - a)
+    Solve for t, keeping only those that fit betwee 0 < t < 1
+*/
+int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
+{
+#ifdef SK_SCALAR_IS_FIXED
+    if (!is_not_monotonic(a, b, c, d))
+        return 0;
+#endif
+
+    // we divide A,B,C by 3 to simplify
+    SkScalar A = d - a + 3*(b - c);
+    SkScalar B = 2*(a - b - b + c);
+    SkScalar C = b - a;
+
+    return SkFindUnitQuadRoots(A, B, C, tValues);
+}
+
+static void interp_cubic_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
+{
+    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
+    SkScalar    cd = SkScalarInterp(src[4], src[6], t);
+    SkScalar    abc = SkScalarInterp(ab, bc, t);
+    SkScalar    bcd = SkScalarInterp(bc, cd, t);
+    SkScalar    abcd = SkScalarInterp(abc, bcd, t);
+
+    dst[0] = src[0];
+    dst[2] = ab;
+    dst[4] = abc;
+    dst[6] = abcd;
+    dst[8] = bcd;
+    dst[10] = cd;
+    dst[12] = src[6];
+}
+
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
+{
+    SkASSERT(t > 0 && t < SK_Scalar1);
+
+    interp_cubic_coords(&src[0].fX, &dst[0].fX, t);
+    interp_cubic_coords(&src[0].fY, &dst[0].fY, t);
+}
+
+/*  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
+    (thanks to finite float precision and rounding in the subtracts). Thus
+    even though the 2nd tValue looks < 1.0, after we renormalize it, we end
+    up with 1.0, hence the need to check and just return the last cubic as
+    a degenerate clump of 4 points in the sampe place.
+
+    static void test_cubic() {
+        SkPoint src[4] = {
+            { 556.25000, 523.03003 },
+            { 556.23999, 522.96002 },
+            { 556.21997, 522.89001 },
+            { 556.21997, 522.82001 }
+        };
+        SkPoint dst[10];
+        SkScalar tval[] = { 0.33333334f, 0.99999994f };
+        SkChopCubicAt(src, dst, tval, 2);
+    }
+ */
+
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int roots)
+{
+#ifdef SK_DEBUG
+    {
+        for (int i = 0; i < roots - 1; i++)
+        {
+            SkASSERT(is_unit_interval(tValues[i]));
+            SkASSERT(is_unit_interval(tValues[i+1]));
+            SkASSERT(tValues[i] < tValues[i+1]);
+        }
+    }
+#endif
+
+    if (dst)
+    {
+        if (roots == 0) // nothing to chop
+            memcpy(dst, src, 4*sizeof(SkPoint));
+        else
+        {
+            SkScalar    t = tValues[0];
+            SkPoint     tmp[4];
+
+            for (int i = 0; i < roots; i++)
+            {
+                SkChopCubicAt(src, dst, t);
+                if (i == roots - 1)
+                    break;
+
+                dst += 3;
+                // have src point to the remaining cubic (after the chop)
+                memcpy(tmp, dst, 4 * sizeof(SkPoint));
+                src = tmp;
+
+                // watch out in case the renormalized t isn't in range
+                if (!valid_unit_divide(tValues[i+1] - tValues[i],
+                                       SK_Scalar1 - tValues[i], &t)) {
+                    // if we can't, just create a degenerate cubic
+                    dst[4] = dst[5] = dst[6] = src[3];
+                    break;
+                }
+            }
+        }
+    }
+}
+
+void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7])
+{
+    SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
+    SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
+    SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
+    SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
+    SkScalar x23 = SkScalarAve(src[2].fX, src[3].fX);
+    SkScalar y23 = SkScalarAve(src[2].fY, src[3].fY);
+
+    SkScalar x012 = SkScalarAve(x01, x12);
+    SkScalar y012 = SkScalarAve(y01, y12);
+    SkScalar x123 = SkScalarAve(x12, x23);
+    SkScalar y123 = SkScalarAve(y12, y23);
+
+    dst[0] = src[0];
+    dst[1].set(x01, y01);
+    dst[2].set(x012, y012);
+    dst[3].set(SkScalarAve(x012, x123), SkScalarAve(y012, y123));
+    dst[4].set(x123, y123);
+    dst[5].set(x23, y23);
+    dst[6] = src[3];
+}
+
+static void flatten_double_cubic_extrema(SkScalar coords[14])
+{
+    coords[4] = coords[8] = coords[6];
+}
+
+/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
+    the resulting beziers are monotonic in Y. This is called by the scan converter.
+    Depending on what is returned, dst[] is treated as follows
+    0   dst[0..3] is the original cubic
+    1   dst[0..3] and dst[3..6] are the two new cubics
+    2   dst[0..3], dst[3..6], dst[6..9] are the three new cubics
+    If dst == null, it is ignored and only the count is returned.
+*/
+int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) {
+    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
+        flatten_double_cubic_extrema(&dst[0].fY);
+        if (roots == 2) {
+            flatten_double_cubic_extrema(&dst[3].fY);
+        }
+    }
+    return roots;
+}
+
+int SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10]) {
+    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
+        flatten_double_cubic_extrema(&dst[0].fX);
+        if (roots == 2) {
+            flatten_double_cubic_extrema(&dst[3].fX);
+        }
+    }
+    return roots;
+}
+
+/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html
+
+    Inflection means that curvature is zero.
+    Curvature is [F' x F''] / [F'^3]
+    So we solve F'x X F''y - F'y X F''y == 0
+    After some canceling of the cubic term, we get
+    A = b - a
+    B = c - 2b + a
+    C = d - 3c + 3b - a
+    (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0
+*/
+int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[])
+{
+    SkScalar    Ax = src[1].fX - src[0].fX;
+    SkScalar    Ay = src[1].fY - src[0].fY;
+    SkScalar    Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
+    SkScalar    By = src[2].fY - 2 * src[1].fY + src[0].fY;
+    SkScalar    Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
+    SkScalar    Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
+    int         count;
+
+#ifdef SK_SCALAR_IS_FLOAT
+    count = SkFindUnitQuadRoots(Bx*Cy - By*Cx, Ax*Cy - Ay*Cx, Ax*By - Ay*Bx, tValues);
+#else
+    Sk64    A, B, C, tmp;
+
+    A.setMul(Bx, Cy);
+    tmp.setMul(By, Cx);
+    A.sub(tmp);
+
+    B.setMul(Ax, Cy);
+    tmp.setMul(Ay, Cx);
+    B.sub(tmp);
+
+    C.setMul(Ax, By);
+    tmp.setMul(Ay, Bx);
+    C.sub(tmp);
+
+    count = Sk64FindFixedQuadRoots(A, B, C, tValues);
+#endif
+
+    return count;
+}
+
+int SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10])
+{
+    SkScalar    tValues[2];
+    int         count = SkFindCubicInflections(src, tValues);
+
+    if (dst)
+    {
+        if (count == 0)
+            memcpy(dst, src, 4 * sizeof(SkPoint));
+        else
+            SkChopCubicAt(src, dst, tValues, count);
+    }
+    return count + 1;
+}
+
+template <typename T> void bubble_sort(T array[], int count)
+{
+    for (int i = count - 1; i > 0; --i)
+        for (int j = i; j > 0; --j)
+            if (array[j] < array[j-1])
+            {
+                T   tmp(array[j]);
+                array[j] = array[j-1];
+                array[j-1] = tmp;
+            }
+}
+
+#include "SkFP.h"
+
+// newton refinement
+#if 0
+static SkScalar refine_cubic_root(const SkFP coeff[4], SkScalar root)
+{
+    //  x1 = x0 - f(t) / f'(t)
+
+    SkFP    T = SkScalarToFloat(root);
+    SkFP    N, D;
+
+    // f' = 3*coeff[0]*T^2 + 2*coeff[1]*T + coeff[2]
+    D = SkFPMul(SkFPMul(coeff[0], SkFPMul(T,T)), 3);
+    D = SkFPAdd(D, SkFPMulInt(SkFPMul(coeff[1], T), 2));
+    D = SkFPAdd(D, coeff[2]);
+
+    if (D == 0)
+        return root;
+
+    // f = coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3]
+    N = SkFPMul(SkFPMul(SkFPMul(T, T), T), coeff[0]);
+    N = SkFPAdd(N, SkFPMul(SkFPMul(T, T), coeff[1]));
+    N = SkFPAdd(N, SkFPMul(T, coeff[2]));
+    N = SkFPAdd(N, coeff[3]);
+
+    if (N)
+    {
+        SkScalar delta = SkFPToScalar(SkFPDiv(N, D));
+
+        if (delta)
+            root -= delta;
+    }
+    return root;
+}
+#endif
+
+/**
+ *  Given an array and count, remove all pair-wise duplicates from the array,
+ *  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) {
+                array[i - 1] = array[i];
+            }
+            count -= 1;
+        } else {
+            array += 1;
+        }
+    }
+    return count;
+}
+
+#ifdef SK_DEBUG
+
+#define TEST_COLLAPS_ENTRY(array)   array, SK_ARRAY_COUNT(array)
+
+static void test_collaps_duplicates() {
+    static bool gOnce;
+    if (gOnce) { return; }
+    gOnce = true;
+    const float src0[] = { 0 };
+    const float src1[] = { 0, 0 };
+    const float src2[] = { 0, 1 };
+    const float src3[] = { 0, 0, 0 };
+    const float src4[] = { 0, 0, 1 };
+    const float src5[] = { 0, 1, 1 };
+    const float src6[] = { 0, 1, 2 };
+    const struct {
+        const float* fData;
+        int fCount;
+        int fCollapsedCount;
+    } data[] = {
+        { TEST_COLLAPS_ENTRY(src0), 1 },
+        { TEST_COLLAPS_ENTRY(src1), 1 },
+        { TEST_COLLAPS_ENTRY(src2), 2 },
+        { TEST_COLLAPS_ENTRY(src3), 1 },
+        { TEST_COLLAPS_ENTRY(src4), 2 },
+        { TEST_COLLAPS_ENTRY(src5), 2 },
+        { TEST_COLLAPS_ENTRY(src6), 3 },
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(data); ++i) {
+        float dst[3];
+        memcpy(dst, data[i].fData, data[i].fCount * sizeof(dst[0]));
+        int count = collaps_duplicates(dst, data[i].fCount);
+        SkASSERT(data[i].fCollapsedCount == count);
+        for (int j = 1; j < count; ++j) {
+            SkASSERT(dst[j-1] < dst[j]);
+        }
+    }
+}
+#endif
+
+#if defined _WIN32 && _MSC_VER >= 1300  && defined SK_SCALAR_IS_FIXED // disable warning : unreachable code if building fixed point for windows desktop
+#pragma warning ( disable : 4702 )
+#endif
+
+/*  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.
+*/
+static int solve_cubic_polynomial(const SkFP coeff[4], SkScalar tValues[3])
+{
+#ifndef SK_SCALAR_IS_FLOAT
+    return 0;   // this is not yet implemented for software float
+#endif
+
+    if (SkScalarNearlyZero(coeff[0]))   // we're just a quadratic
+    {
+        return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
+    }
+
+    SkFP    a, b, c, Q, R;
+
+    {
+        SkASSERT(coeff[0] != 0);
+
+        SkFP inva = SkFPInvert(coeff[0]);
+        a = SkFPMul(coeff[1], inva);
+        b = SkFPMul(coeff[2], inva);
+        c = SkFPMul(coeff[3], inva);
+    }
+    Q = SkFPDivInt(SkFPSub(SkFPMul(a,a), SkFPMulInt(b, 3)), 9);
+//  R = (2*a*a*a - 9*a*b + 27*c) / 54;
+    R = SkFPMulInt(SkFPMul(SkFPMul(a, a), a), 2);
+    R = SkFPSub(R, SkFPMulInt(SkFPMul(a, b), 9));
+    R = SkFPAdd(R, SkFPMulInt(c, 27));
+    R = SkFPDivInt(R, 54);
+
+    SkFP Q3 = SkFPMul(SkFPMul(Q, Q), Q);
+    SkFP R2MinusQ3 = SkFPSub(SkFPMul(R,R), Q3);
+    SkFP adiv3 = SkFPDivInt(a, 3);
+
+    SkScalar*   roots = tValues;
+    SkScalar    r;
+
+    if (SkFPLT(R2MinusQ3, 0))   // we have 3 real roots
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float theta = sk_float_acos(R / sk_float_sqrt(Q3));
+        float neg2RootQ = -2 * sk_float_sqrt(Q);
+
+        r = neg2RootQ * sk_float_cos(theta/3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        r = neg2RootQ * sk_float_cos((theta + 2*SK_ScalarPI)/3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        r = neg2RootQ * sk_float_cos((theta - 2*SK_ScalarPI)/3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        SkDEBUGCODE(test_collaps_duplicates();)
+
+        // now sort the roots
+        int count = (int)(roots - tValues);
+        SkASSERT((unsigned)count <= 3);
+        bubble_sort(tValues, count);
+        count = collaps_duplicates(tValues, count);
+        roots = tValues + count;    // so we compute the proper count below
+#endif
+    }
+    else                // we have 1 real root
+    {
+        SkFP A = SkFPAdd(SkFPAbs(R), SkFPSqrt(R2MinusQ3));
+        A = SkFPCubeRoot(A);
+        if (SkFPGT(R, 0))
+            A = SkFPNeg(A);
+
+        if (A != 0)
+            A = SkFPAdd(A, SkFPDiv(Q, A));
+        r = SkFPToScalar(SkFPSub(A, adiv3));
+        if (is_unit_interval(r))
+            *roots++ = r;
+    }
+
+    return (int)(roots - tValues);
+}
+
+/*  Looking for F' dot F'' == 0
+    
+    A = b - a
+    B = c - 2b + a
+    C = d - 3c + 3b - a
+
+    F' = 3Ct^2 + 6Bt + 3A
+    F'' = 6Ct + 6B
+
+    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
+*/
+static void formulate_F1DotF2(const SkScalar src[], SkFP coeff[4])
+{
+    SkScalar    a = src[2] - src[0];
+    SkScalar    b = src[4] - 2 * src[2] + src[0];
+    SkScalar    c = src[6] + 3 * (src[2] - src[4]) - src[0];
+
+    SkFP    A = SkScalarToFP(a);
+    SkFP    B = SkScalarToFP(b);
+    SkFP    C = SkScalarToFP(c);
+
+    coeff[0] = SkFPMul(C, C);
+    coeff[1] = SkFPMulInt(SkFPMul(B, C), 3);
+    coeff[2] = SkFPMulInt(SkFPMul(B, B), 2);
+    coeff[2] = SkFPAdd(coeff[2], SkFPMul(C, A));
+    coeff[3] = SkFPMul(A, B);
+}
+
+// EXPERIMENTAL: can set this to zero to accept all t-values 0 < t < 1
+//#define kMinTValueForChopping (SK_Scalar1 / 256)
+#define kMinTValueForChopping   0
+
+/*  Looking for F' dot F'' == 0
+    
+    A = b - a
+    B = c - 2b + a
+    C = d - 3c + 3b - a
+
+    F' = 3Ct^2 + 6Bt + 3A
+    F'' = 6Ct + 6B
+
+    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
+*/
+int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])
+{
+    SkFP    coeffX[4], coeffY[4];
+    int     i;
+
+    formulate_F1DotF2(&src[0].fX, coeffX);
+    formulate_F1DotF2(&src[0].fY, coeffY);
+
+    for (i = 0; i < 4; i++)
+        coeffX[i] = SkFPAdd(coeffX[i],coeffY[i]);
+
+    SkScalar    t[3];
+    int         count = solve_cubic_polynomial(coeffX, t);
+    int         maxCount = 0;
+
+    // now remove extrema where the curvature is zero (mins)
+    // !!!! need a test for this !!!!
+    for (i = 0; i < count; i++)
+    {
+        // if (not_min_curvature())
+        if (t[i] > kMinTValueForChopping && t[i] < SK_Scalar1 - kMinTValueForChopping)
+            tValues[maxCount++] = t[i];
+    }
+    return maxCount;
+}
+
+int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
+{
+    SkScalar    t_storage[3];
+
+    if (tValues == NULL)
+        tValues = t_storage;
+
+    int count = SkFindCubicMaxCurvature(src, tValues);
+
+    if (dst)
+    {
+        if (count == 0)
+            memcpy(dst, src, 4 * sizeof(SkPoint));
+        else
+            SkChopCubicAt(src, dst, tValues, count);
+    }
+    return count + 1;
+}
+
+bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous) {
+    if (ambiguous) {
+        *ambiguous = false;
+    }
+
+    // Find the minimum and maximum y of the extrema, which are the
+    // first and last points since this cubic is monotonic
+    SkScalar min_y = SkMinScalar(cubic[0].fY, cubic[3].fY);
+    SkScalar max_y = SkMaxScalar(cubic[0].fY, cubic[3].fY);
+
+    if (pt.fY == cubic[0].fY
+        || pt.fY < min_y
+        || pt.fY > max_y) {
+        // The query line definitely does not cross the curve
+        if (ambiguous) {
+            *ambiguous = (pt.fY == cubic[0].fY);
+        }
+        return false;
+    }
+
+    bool pt_at_extremum = (pt.fY == cubic[3].fY);
+
+    SkScalar min_x =
+        SkMinScalar(
+            SkMinScalar(
+                SkMinScalar(cubic[0].fX, cubic[1].fX),
+                cubic[2].fX),
+            cubic[3].fX);
+    if (pt.fX < min_x) {
+        // The query line definitely crosses the curve
+        if (ambiguous) {
+            *ambiguous = pt_at_extremum;
+        }
+        return true;
+    }
+
+    SkScalar max_x =
+        SkMaxScalar(
+            SkMaxScalar(
+                SkMaxScalar(cubic[0].fX, cubic[1].fX),
+                cubic[2].fX),
+            cubic[3].fX);
+    if (pt.fX > max_x) {
+        // The query line definitely does not cross the curve
+        return false;
+    }
+
+    // Do a binary search to find the parameter value which makes y as
+    // close as possible to the query point. See whether the query
+    // line's origin is to the left of the associated x coordinate.
+
+    // kMaxIter is chosen as the number of mantissa bits for a float,
+    // since there's no way we are going to get more precision by
+    // iterating more times than that.
+    const int kMaxIter = 23;
+    SkPoint eval;
+    int iter = 0;
+    SkScalar upper_t;
+    SkScalar lower_t;
+    // Need to invert direction of t parameter if cubic goes up
+    // instead of down
+    if (cubic[3].fY > cubic[0].fY) {
+        upper_t = SK_Scalar1;
+        lower_t = SkFloatToScalar(0);
+    } else {
+        upper_t = SkFloatToScalar(0);
+        lower_t = SK_Scalar1;
+    }
+    do {
+        SkScalar t = SkScalarAve(upper_t, lower_t);
+        SkEvalCubicAt(cubic, t, &eval, NULL, NULL);
+        if (pt.fY > eval.fY) {
+            lower_t = t;
+        } else {
+            upper_t = t;
+        }
+    } while (++iter < kMaxIter
+             && !SkScalarNearlyZero(eval.fY - pt.fY));
+    if (pt.fX <= eval.fX) {
+        if (ambiguous) {
+            *ambiguous = pt_at_extremum;
+        }
+        return true;
+    }
+    return false;
+}
+
+int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous) {
+    int num_crossings = 0;
+    SkPoint monotonic_cubics[10];
+    int num_monotonic_cubics = SkChopCubicAtYExtrema(cubic, monotonic_cubics);
+    if (ambiguous) {
+        *ambiguous = false;
+    }
+    bool locally_ambiguous;
+    if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[0], &locally_ambiguous))
+        ++num_crossings;
+    if (ambiguous) {
+        *ambiguous |= locally_ambiguous;
+    }
+    if (num_monotonic_cubics > 0)
+        if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[3], &locally_ambiguous))
+            ++num_crossings;
+    if (ambiguous) {
+        *ambiguous |= locally_ambiguous;
+    }
+    if (num_monotonic_cubics > 1)
+        if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[6], &locally_ambiguous))
+            ++num_crossings;
+    if (ambiguous) {
+        *ambiguous |= locally_ambiguous;
+    }
+    return num_crossings;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*  Find t value for quadratic [a, b, c] = d.
+    Return 0 if there is no solution within [0, 1)
+*/
+static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d)
+{
+    // At^2 + Bt + C = d
+    SkScalar A = a - 2 * b + c;
+    SkScalar B = 2 * (b - a);
+    SkScalar C = a - d;
+
+    SkScalar    roots[2];
+    int         count = SkFindUnitQuadRoots(A, B, C, roots);
+
+    SkASSERT(count <= 1);
+    return count == 1 ? roots[0] : 0;
+}
+
+/*  given a quad-curve and a point (x,y), chop the quad at that point and return
+    the new quad's offCurve point. Should only return false if the computed pos
+    is the start of the curve (i.e. root == 0)
+*/
+static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve)
+{
+    const SkScalar* base;
+    SkScalar        value;
+
+    if (SkScalarAbs(x) < SkScalarAbs(y)) {
+        base = &quad[0].fX;
+        value = x;
+    } else {
+        base = &quad[0].fY;
+        value = y;
+    }
+
+    // note: this returns 0 if it thinks value is out of range, meaning the
+    // root might return something outside of [0, 1)
+    SkScalar t = quad_solve(base[0], base[2], base[4], value);
+
+    if (t > 0)
+    {
+        SkPoint tmp[5];
+        SkChopQuadAt(quad, tmp, t);
+        *offCurve = tmp[1];
+        return true;
+    } else {
+        /*  t == 0 means either the value triggered a root outside of [0, 1)
+            For our purposes, we can ignore the <= 0 roots, but we want to
+            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
+            (depending on the direction/sign of the end points).
+        */
+        if ((base[0] < base[4] && value > base[2]) ||
+            (base[0] > base[4] && value < base[2]))   // should root have been 1
+        {
+            *offCurve = quad[1];
+            return true;
+        }
+    }
+    return false;
+}
+
+static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = {
+    { SK_Scalar1,           0               },
+    { SK_Scalar1,           SK_ScalarTanPIOver8 },
+    { SK_ScalarRoot2Over2,  SK_ScalarRoot2Over2 },
+    { SK_ScalarTanPIOver8,  SK_Scalar1          },
+
+    { 0,                    SK_Scalar1      },
+    { -SK_ScalarTanPIOver8, SK_Scalar1  },
+    { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 },
+    { -SK_Scalar1,          SK_ScalarTanPIOver8 },
+
+    { -SK_Scalar1,          0               },
+    { -SK_Scalar1,          -SK_ScalarTanPIOver8    },
+    { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2    },
+    { -SK_ScalarTanPIOver8, -SK_Scalar1     },
+
+    { 0,                    -SK_Scalar1     },
+    { SK_ScalarTanPIOver8,  -SK_Scalar1     },
+    { SK_ScalarRoot2Over2,  -SK_ScalarRoot2Over2    },
+    { SK_Scalar1,           -SK_ScalarTanPIOver8    },
+
+    { SK_Scalar1,           0   }
+};
+
+int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop,
+                   SkRotationDirection dir, const SkMatrix* userMatrix,
+                   SkPoint quadPoints[])
+{
+    // rotate by x,y so that uStart is (1.0)
+    SkScalar x = SkPoint::DotProduct(uStart, uStop);
+    SkScalar y = SkPoint::CrossProduct(uStart, uStop);
+
+    SkScalar absX = SkScalarAbs(x);
+    SkScalar absY = SkScalarAbs(y);
+
+    int pointCount;
+
+    // check for (effectively) coincident vectors
+    // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
+    // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
+    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;
+    } else {
+        if (dir == kCCW_SkRotationDirection)
+            y = -y;
+
+        // what octant (quadratic curve) is [xy] in?
+        int oct = 0;
+        bool sameSign = true;
+
+        if (0 == y)
+        {
+            oct = 4;        // 180
+            SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
+        }
+        else if (0 == x)
+        {
+            SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
+            if (y > 0)
+                oct = 2;    // 90
+            else
+                oct = 6;    // 270
+        }
+        else
+        {
+            if (y < 0)
+                oct += 4;
+            if ((x < 0) != (y < 0))
+            {
+                oct += 2;
+                sameSign = false;
+            }
+            if ((absX < absY) == sameSign)
+                oct += 1;
+        }
+
+        int wholeCount = oct << 1;
+        memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint));
+
+        const SkPoint* arc = &gQuadCirclePts[wholeCount];
+        if (quad_pt2OffCurve(arc, x, y, &quadPoints[wholeCount + 1]))
+        {
+            quadPoints[wholeCount + 2].set(x, y);
+            wholeCount += 2;
+        }
+        pointCount = wholeCount + 1;
+    }
+
+    // now handle counter-clockwise and the initial unitStart rotation
+    SkMatrix    matrix;
+    matrix.setSinCos(uStart.fY, uStart.fX);
+    if (dir == kCCW_SkRotationDirection) {
+        matrix.preScale(SK_Scalar1, -SK_Scalar1);
+    }
+    if (userMatrix) {
+        matrix.postConcat(*userMatrix);
+    }
+    matrix.mapPoints(quadPoints, pointCount);
+    return pointCount;
+}
+
diff --git a/legacy/src/core/SkGlyphCache.cpp b/legacy/src/core/SkGlyphCache.cpp
new file mode 100644
index 0000000..9f0dfba
--- /dev/null
+++ b/legacy/src/core/SkGlyphCache.cpp
@@ -0,0 +1,667 @@
+
+/*
+ * 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 "SkGlyphCache.h"
+#include "SkGraphics.h"
+#include "SkPaint.h"
+#include "SkTemplates.h"
+
+//#define SPEW_PURGE_STATUS
+//#define USE_CACHE_HASH
+//#define RECORD_HASH_EFFICIENCY
+
+bool gSkSuppressFontCachePurgeSpew;
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef RECORD_HASH_EFFICIENCY
+    static uint32_t gHashSuccess;
+    static uint32_t gHashCollision;
+
+    static void RecordHashSuccess() {
+        gHashSuccess += 1;
+    }
+
+    static void RecordHashCollisionIf(bool pred) {
+        if (pred) {
+            gHashCollision += 1;
+
+            uint32_t total = gHashSuccess + gHashCollision;
+            SkDebugf("Font Cache Hash success rate: %d%%\n",
+                     100 * gHashSuccess / total);
+        }
+    }
+#else
+    #define RecordHashSuccess() (void)0
+    #define RecordHashCollisionIf(pred) (void)0
+#endif
+#define RecordHashCollision() RecordHashCollisionIf(true)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define kMinGlphAlloc       (sizeof(SkGlyph) * 64)
+#define kMinImageAlloc      (24 * 64)   // should be pointsize-dependent
+
+#define METRICS_RESERVE_COUNT  128  // so we don't grow this array a lot
+
+SkGlyphCache::SkGlyphCache(const SkDescriptor* desc)
+        : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) {
+    fPrev = fNext = NULL;
+
+    fDesc = desc->copy();
+    fScalerContext = SkScalerContext::Create(desc);
+    fScalerContext->getFontMetrics(NULL, &fFontMetricsY);
+
+    // init to 0 so that all of the pointers will be null
+    memset(fGlyphHash, 0, sizeof(fGlyphHash));
+    // init with 0xFF so that the charCode field will be -1, which is invalid
+    memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
+
+    fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc;
+
+    fGlyphArray.setReserve(METRICS_RESERVE_COUNT);
+
+    fMetricsCount = 0;
+    fAdvanceCount = 0;
+    fAuxProcList = NULL;
+}
+
+SkGlyphCache::~SkGlyphCache() {
+    SkGlyph**   gptr = fGlyphArray.begin();
+    SkGlyph**   stop = fGlyphArray.end();
+    while (gptr < stop) {
+        SkPath* path = (*gptr)->fPath;
+        if (path) {
+            SkDELETE(path);
+        }
+        gptr += 1;
+    }
+    SkDescriptor::Free(fDesc);
+    SkDELETE(fScalerContext);
+    this->invokeAndRemoveAuxProcs();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+#define VALIDATE()  AutoValidate av(this)
+#else
+#define VALIDATE()
+#endif
+
+uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode);
+    const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
+
+    if (rec.fID == id) {
+        return rec.fGlyph->getGlyphID();
+    } else {
+        return fScalerContext->charToGlyphID(charCode);
+    }
+}
+
+SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
+    return fScalerContext->glyphIDToChar(glyphID);
+}
+
+unsigned SkGlyphCache::getGlyphCount() {
+    return fScalerContext->getGlyphCount();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode);
+    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+
+    if (rec->fID != id) {
+        // this ID is based on the UniChar
+        rec->fID = id;
+        // this ID is based on the glyph index
+        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
+        rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
+    }
+    return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(glyphID);
+    unsigned index = ID2HashIndex(id);
+    SkGlyph* glyph = fGlyphHash[index];
+
+    if (NULL == glyph || glyph->fID != id) {
+        glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
+        fGlyphHash[index] = glyph;
+    }
+    return *glyph;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode);
+    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+
+    if (rec->fID != id) {
+        RecordHashCollisionIf(rec->fGlyph != NULL);
+        // this ID is based on the UniChar
+        rec->fID = id;
+        // this ID is based on the glyph index
+        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
+        rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
+    } else {
+        RecordHashSuccess();
+        if (rec->fGlyph->isJustAdvance()) {
+            fScalerContext->getMetrics(rec->fGlyph);
+        }
+    }
+    SkASSERT(rec->fGlyph->isFullMetrics());
+    return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
+                                               SkFixed x, SkFixed y) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode, x, y);
+    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+
+    if (rec->fID != id) {
+        RecordHashCollisionIf(rec->fGlyph != NULL);
+        // this ID is based on the UniChar
+        rec->fID = id;
+        // this ID is based on the glyph index
+        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
+        rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
+    } else {
+        RecordHashSuccess();
+        if (rec->fGlyph->isJustAdvance()) {
+            fScalerContext->getMetrics(rec->fGlyph);
+        }
+    }
+    SkASSERT(rec->fGlyph->isFullMetrics());
+    return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(glyphID);
+    unsigned index = ID2HashIndex(id);
+    SkGlyph* glyph = fGlyphHash[index];
+
+    if (NULL == glyph || glyph->fID != id) {
+        RecordHashCollisionIf(glyph != NULL);
+        glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
+        fGlyphHash[index] = glyph;
+    } else {
+        RecordHashSuccess();
+        if (glyph->isJustAdvance()) {
+            fScalerContext->getMetrics(glyph);
+        }
+    }
+    SkASSERT(glyph->isFullMetrics());
+    return *glyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
+                                               SkFixed x, SkFixed y) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(glyphID, x, y);
+    unsigned index = ID2HashIndex(id);
+    SkGlyph* glyph = fGlyphHash[index];
+
+    if (NULL == glyph || glyph->fID != id) {
+        RecordHashCollisionIf(glyph != NULL);
+        glyph = this->lookupMetrics(id, kFull_MetricsType);
+        fGlyphHash[index] = glyph;
+    } else {
+        RecordHashSuccess();
+        if (glyph->isJustAdvance()) {
+            fScalerContext->getMetrics(glyph);
+        }
+    }
+    SkASSERT(glyph->isFullMetrics());
+    return *glyph;
+}
+
+SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
+    SkGlyph* glyph;
+
+    int     hi = 0;
+    int     count = fGlyphArray.count();
+
+    if (count) {
+        SkGlyph**   gptr = fGlyphArray.begin();
+        int     lo = 0;
+
+        hi = count - 1;
+        while (lo < hi) {
+            int mid = (hi + lo) >> 1;
+            if (gptr[mid]->fID < id) {
+                lo = mid + 1;
+            } else {
+                hi = mid;
+            }
+        }
+        glyph = gptr[hi];
+        if (glyph->fID == id) {
+            if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
+                fScalerContext->getMetrics(glyph);
+            }
+            return glyph;
+        }
+
+        // check if we need to bump hi before falling though to the allocator
+        if (glyph->fID < id) {
+            hi += 1;
+        }
+    }
+
+    // not found, but hi tells us where to inser the new glyph
+    fMemoryUsed += sizeof(SkGlyph);
+
+    glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
+                                        SkChunkAlloc::kThrow_AllocFailType);
+    glyph->init(id);
+    *fGlyphArray.insert(hi) = glyph;
+
+    if (kJustAdvance_MetricsType == mtype) {
+        fScalerContext->getAdvance(glyph);
+        fAdvanceCount += 1;
+    } else {
+        SkASSERT(kFull_MetricsType == mtype);
+        fScalerContext->getMetrics(glyph);
+        fMetricsCount += 1;
+    }
+
+    return glyph;
+}
+
+const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
+    if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
+        if (glyph.fImage == NULL) {
+            size_t  size = glyph.computeImageSize();
+            const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size,
+                                        SkChunkAlloc::kReturnNil_AllocFailType);
+            // check that alloc() actually succeeded
+            if (glyph.fImage) {
+                fScalerContext->getImage(glyph);
+                // TODO: the scaler may have changed the maskformat during
+                // getImage (e.g. from AA or LCD to BW) which means we may have
+                // overallocated the buffer. Check if the new computedImageSize
+                // is smaller, and if so, strink the alloc size in fImageAlloc.
+                fMemoryUsed += size;
+            }
+        }
+    }
+    return glyph.fImage;
+}
+
+const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
+    if (glyph.fWidth) {
+        if (glyph.fPath == NULL) {
+            const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
+            fScalerContext->getPath(glyph, glyph.fPath);
+            fMemoryUsed += sizeof(SkPath) +
+                    glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint);
+        }
+    }
+    return glyph.fPath;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
+    const AuxProcRec* rec = fAuxProcList;
+    while (rec) {
+        if (rec->fProc == proc) {
+            if (dataPtr) {
+                *dataPtr = rec->fData;
+            }
+            return true;
+        }
+        rec = rec->fNext;
+    }
+    return false;
+}
+
+void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
+    if (proc == NULL) {
+        return;
+    }
+
+    AuxProcRec* rec = fAuxProcList;
+    while (rec) {
+        if (rec->fProc == proc) {
+            rec->fData = data;
+            return;
+        }
+        rec = rec->fNext;
+    }
+    // not found, create a new rec
+    rec = SkNEW(AuxProcRec);
+    rec->fProc = proc;
+    rec->fData = data;
+    rec->fNext = fAuxProcList;
+    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) {
+        rec->fProc(rec->fData);
+        AuxProcRec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef USE_CACHE_HASH
+    #define HASH_BITCOUNT   6
+    #define HASH_COUNT      (1 << HASH_BITCOUNT)
+    #define HASH_MASK       (HASH_COUNT - 1)
+
+    static unsigned desc_to_hashindex(const SkDescriptor* desc)
+    {
+        SkASSERT(HASH_MASK < 256);  // since our munging reduces to 8 bits
+
+        uint32_t n = *(const uint32_t*)desc;    //desc->getChecksum();
+        SkASSERT(n == desc->getChecksum());
+
+        // don't trust that the low bits of checksum vary enough, so...
+        n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30);
+
+        return n & HASH_MASK;
+    }
+#endif
+
+#include "SkThread.h"
+
+class SkGlyphCache_Globals {
+public:
+    SkGlyphCache_Globals() {
+        fHead = NULL;
+        fTotalMemoryUsed = 0;
+#ifdef USE_CACHE_HASH
+        sk_bzero(fHash, sizeof(fHash));
+#endif
+    }
+
+    SkMutex         fMutex;
+    SkGlyphCache*   fHead;
+    size_t          fTotalMemoryUsed;
+#ifdef USE_CACHE_HASH
+    SkGlyphCache*   fHash[HASH_COUNT];
+#endif
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+};
+
+static SkGlyphCache_Globals& getGlobals() {
+    // we leak this, so we don't incur any shutdown cost of the destructor
+    static SkGlyphCache_Globals* gGlobals = new SkGlyphCache_Globals;
+    return *gGlobals;
+}
+
+void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
+                                  void* context) {
+    SkGlyphCache_Globals& globals = getGlobals();
+    SkAutoMutexAcquire    ac(globals.fMutex);
+    SkGlyphCache*         cache;
+
+    globals.validate();
+
+    for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
+        if (proc(cache, context)) {
+            break;
+        }
+    }
+
+    globals.validate();
+}
+
+/*  This guy calls the visitor from within the mutext lock, so the visitor
+    cannot:
+    - take too much time
+    - try to acquire the mutext again
+    - call a fontscaler (which might call into the cache)
+*/
+SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc,
+                              bool (*proc)(const SkGlyphCache*, void*),
+                              void* context) {
+    SkASSERT(desc);
+
+    SkGlyphCache_Globals& globals = getGlobals();
+    SkAutoMutexAcquire    ac(globals.fMutex);
+    SkGlyphCache*         cache;
+    bool                  insideMutex = true;
+
+    globals.validate();
+
+#ifdef USE_CACHE_HASH
+    SkGlyphCache** hash = globals.fHash;
+    unsigned index = desc_to_hashindex(desc);
+    cache = hash[index];
+    if (cache && *cache->fDesc == *desc) {
+        cache->detach(&globals.fHead);
+        goto FOUND_IT;
+    }
+#endif
+
+    for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
+        if (cache->fDesc->equals(*desc)) {
+            cache->detach(&globals.fHead);
+            goto FOUND_IT;
+        }
+    }
+
+    /* Release the mutex now, before we create a new entry (which might have
+        side-effects like trying to access the cache/mutex (yikes!)
+    */
+    ac.release();           // release the mutex now
+    insideMutex = false;    // can't use globals anymore
+
+    cache = SkNEW_ARGS(SkGlyphCache, (desc));
+
+FOUND_IT:
+
+    AutoValidate av(cache);
+
+    if (proc(cache, context)) {   // stay detached
+        if (insideMutex) {
+            SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed);
+            globals.fTotalMemoryUsed -= cache->fMemoryUsed;
+#ifdef USE_CACHE_HASH
+            hash[index] = NULL;
+#endif
+        }
+    } else {                        // reattach
+        if (insideMutex) {
+            cache->attachToHead(&globals.fHead);
+#ifdef USE_CACHE_HASH
+            hash[index] = cache;
+#endif
+        } else {
+            AttachCache(cache);
+        }
+        cache = NULL;
+    }
+    return cache;
+}
+
+void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
+    SkASSERT(cache);
+    SkASSERT(cache->fNext == NULL);
+
+    SkGlyphCache_Globals& globals = getGlobals();
+    SkAutoMutexAcquire    ac(globals.fMutex);
+
+    globals.validate();
+    cache->validate();
+
+    // 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();
+        if (allocated > budgeted) {
+            (void)InternalFreeCache(&globals, allocated - budgeted);
+        }
+    }
+
+    cache->attachToHead(&globals.fHead);
+    globals.fTotalMemoryUsed += cache->fMemoryUsed;
+
+#ifdef USE_CACHE_HASH
+    unsigned index = desc_to_hashindex(cache->fDesc);
+    SkASSERT(globals.fHash[index] != cache);
+    globals.fHash[index] = cache;
+#endif
+
+    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) {
+    if (cache) {
+        while (cache->fNext) {
+            cache = cache->fNext;
+        }
+    }
+    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);
+    if (fTotalMemoryUsed != computed) {
+        printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed);
+    }
+    SkASSERT(fTotalMemoryUsed == computed);
+}
+#endif
+
+size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals,
+                                       size_t bytesNeeded) {
+    globals->validate();
+
+    size_t  bytesFreed = 0;
+    int     count = 0;
+
+    // don't do any "small" purges
+    size_t minToPurge = globals->fTotalMemoryUsed >> 2;
+    if (bytesNeeded < minToPurge)
+        bytesNeeded = minToPurge;
+
+    SkGlyphCache* cache = FindTail(globals->fHead);
+    while (cache != NULL && bytesFreed < bytesNeeded) {
+        SkGlyphCache* prev = cache->fPrev;
+        bytesFreed += cache->fMemoryUsed;
+
+#ifdef USE_CACHE_HASH
+        unsigned index = desc_to_hashindex(cache->fDesc);
+        if (cache == globals->fHash[index]) {
+            globals->fHash[index] = NULL;
+        }
+#endif
+
+        cache->detach(&globals->fHead);
+        SkDELETE(cache);
+        cache = prev;
+        count += 1;
+    }
+
+    SkASSERT(bytesFreed <= globals->fTotalMemoryUsed);
+    globals->fTotalMemoryUsed -= bytesFreed;
+    globals->validate();
+
+#ifdef SPEW_PURGE_STATUS
+    if (count && !gSkSuppressFontCachePurgeSpew) {
+        SkDebugf("purging %dK from font cache [%d entries]\n",
+                 (int)(bytesFreed >> 10), count);
+    }
+#endif
+
+    return bytesFreed;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef SK_DEBUG
+
+void SkGlyphCache::validate() const {
+    int count = fGlyphArray.count();
+    for (int i = 0; i < count; i++) {
+        const SkGlyph* glyph = fGlyphArray[i];
+        SkASSERT(glyph);
+        SkASSERT(fGlyphAlloc.contains(glyph));
+        if (glyph->fImage) {
+            SkASSERT(fImageAlloc.contains(glyph->fImage));
+        }
+    }
+}
+
+#endif
diff --git a/legacy/src/core/SkGlyphCache.h b/legacy/src/core/SkGlyphCache.h
new file mode 100644
index 0000000..23f3c55
--- /dev/null
+++ b/legacy/src/core/SkGlyphCache.h
@@ -0,0 +1,317 @@
+
+/*
+ * 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 SkGlyphCache_DEFINED
+#define SkGlyphCache_DEFINED
+
+#include "SkBitmap.h"
+#include "SkChunkAlloc.h"
+#include "SkDescriptor.h"
+#include "SkScalerContext.h"
+#include "SkTemplates.h"
+
+class SkPaint;
+
+class SkGlyphCache_Globals;
+
+/** \class SkGlyphCache
+
+    This class represents a strike: a specific combination of typeface, size,
+    matrix, etc., and holds the glyphs for that strike. Calling any of the
+    getUnichar.../getGlyphID... methods will return the requested glyph,
+    either instantly if it is already cahced, or by first generating it and then
+    adding it to the strike.
+
+    The strikes are held in a global list, available to all threads. To interact
+    with one, call either VisitCache() or DetachCache().
+*/
+class SkGlyphCache {
+public:
+    /** Returns a glyph with valid fAdvance and fDevKern fields.
+        The remaining fields may be valid, but that is not guaranteed. If you
+        require those, call getUnicharMetrics or getGlyphIDMetrics instead.
+    */
+    const SkGlyph& getUnicharAdvance(SkUnichar);
+    const SkGlyph& getGlyphIDAdvance(uint16_t);
+
+    /** Returns a glyph with all fields valid except fImage and fPath, which
+        may be null. If they are null, call findImage or findPath for those.
+        If they are not null, then they are valid.
+
+        This call is potentially slower than the matching ...Advance call. If
+        you only need the fAdvance/fDevKern fields, call those instead.
+    */
+    const SkGlyph& getUnicharMetrics(SkUnichar);
+    const SkGlyph& getGlyphIDMetrics(uint16_t);
+
+    /** These are variants that take the device position of the glyph. Call
+        these only if you are drawing in subpixel mode. Passing 0, 0 is
+        effectively the same as calling the variants w/o the extra params, tho
+        a tiny bit slower.
+    */
+    const SkGlyph& getUnicharMetrics(SkUnichar, SkFixed x, SkFixed y);
+    const SkGlyph& getGlyphIDMetrics(uint16_t, SkFixed x, SkFixed y);
+
+    /** Return the glyphID for the specified Unichar. If the char has already
+        been seen, use the existing cache entry. If not, ask the scalercontext
+        to compute it for us.
+    */
+    uint16_t unicharToGlyph(SkUnichar);
+
+    /** Map the glyph to its Unicode equivalent. Unmappable glyphs map to
+        a character code of zero.
+    */
+    SkUnichar glyphToUnichar(uint16_t);
+
+    /** Returns the number of glyphs for this strike.
+    */
+    unsigned getGlyphCount();
+
+#ifdef SK_BUILD_FOR_ANDROID
+    /** Returns the base glyph count for this strike.
+    */
+    unsigned getBaseGlyphCount(SkUnichar charCode) const {
+        return fScalerContext->getBaseGlyphCount(charCode);
+    }
+#endif
+
+    /** Return the image associated with the glyph. If it has not been generated
+        this will trigger that.
+    */
+    const void* findImage(const SkGlyph&);
+    /** Return the Path associated with the glyph. If it has not been generated
+        this will trigger that.
+    */
+    const SkPath* findPath(const SkGlyph&);
+
+    /** Return the vertical metrics for this strike.
+    */
+    const SkPaint::FontMetrics& getFontMetricsY() const {
+        return fFontMetricsY;
+    }
+
+    const SkDescriptor& getDescriptor() const { return *fDesc; }
+
+    SkMask::Format getMaskFormat() const {
+        return fScalerContext->getMaskFormat();
+    }
+
+    bool isSubpixel() const {
+        return fScalerContext->isSubpixel();
+    }
+
+    /*  AuxProc/Data allow a client to associate data with this cache entry.
+        Multiple clients can use this, as their data is keyed with a function
+        pointer. In addition to serving as a key, the function pointer is called
+        with the data when the glyphcache object is deleted, so the client can
+        cleanup their data as well. NOTE: the auxProc must not try to access
+        this glyphcache in any way, since it may be in the process of being
+        deleted.
+    */
+
+    //! If the proc is found, return true and set *dataPtr to its data
+    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; }
+
+    /** Call proc on all cache entries, stopping early if proc returns true.
+        The proc should not create or delete caches, since it could produce
+        deadlock.
+    */
+    static void VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), void* ctx);
+
+    /** Find a matching cache entry, and call proc() with it. If none is found
+        create a new one. If the proc() returns true, detach the cache and
+        return it, otherwise leave it and return NULL.
+    */
+    static SkGlyphCache* VisitCache(const SkDescriptor* desc,
+                                    bool (*proc)(const SkGlyphCache*, void*),
+                                    void* context);
+
+    /** Given a strike that was returned by either VisitCache() or DetachCache()
+        add it back into the global cache list (after which the caller should
+        not reference it anymore.
+    */
+    static void AttachCache(SkGlyphCache*);
+
+    /** Detach a strike from the global cache matching the specified descriptor.
+        Once detached, it can be queried/modified by the current thread, and
+        when finished, be reattached to the global cache with AttachCache().
+        While detached, if another request is made with the same descriptor,
+        a different strike will be generated. This is fine. It does mean we
+        can have more than 1 strike for the same descriptor, but that will
+        eventually get purged, and the win is that different thread will never
+        block each other while a strike is being used.
+    */
+    static SkGlyphCache* DetachCache(const SkDescriptor* desc) {
+        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
+    void validate() const {}
+#endif
+
+    class AutoValidate : SkNoncopyable {
+    public:
+        AutoValidate(const SkGlyphCache* cache) : fCache(cache) {
+            if (fCache) {
+                fCache->validate();
+            }
+        }
+        ~AutoValidate() {
+            if (fCache) {
+                fCache->validate();
+            }
+        }
+        void forget() {
+            fCache = NULL;
+        }
+    private:
+        const SkGlyphCache* fCache;
+    };
+
+private:
+    SkGlyphCache(const SkDescriptor*);
+    ~SkGlyphCache();
+
+    enum MetricsType {
+        kJustAdvance_MetricsType,
+        kFull_MetricsType
+    };
+
+    SkGlyph* lookupMetrics(uint32_t id, MetricsType);
+    static bool DetachProc(const SkGlyphCache*, void*) { return true; }
+
+    void detach(SkGlyphCache** head) {
+        if (fPrev) {
+            fPrev->fNext = fNext;
+        } else {
+            *head = fNext;
+        }
+        if (fNext) {
+            fNext->fPrev = fPrev;
+        }
+        fPrev = fNext = NULL;
+    }
+
+    void attachToHead(SkGlyphCache** head) {
+        SkASSERT(NULL == fPrev && NULL == fNext);
+        if (*head) {
+            (*head)->fPrev = this;
+            fNext = *head;
+        }
+        *head = this;
+    }
+
+    SkGlyphCache*       fNext, *fPrev;
+    SkDescriptor*       fDesc;
+    SkScalerContext*    fScalerContext;
+    SkPaint::FontMetrics fFontMetricsY;
+
+    enum {
+        kHashBits   = 12,
+        kHashCount  = 1 << kHashBits,
+        kHashMask   = kHashCount - 1
+    };
+    SkGlyph*            fGlyphHash[kHashCount];
+    SkTDArray<SkGlyph*> fGlyphArray;
+    SkChunkAlloc        fGlyphAlloc;
+    SkChunkAlloc        fImageAlloc;
+
+    int fMetricsCount, fAdvanceCount;
+
+    struct CharGlyphRec {
+        uint32_t    fID;    // unichar + subpixel
+        SkGlyph*    fGlyph;
+    };
+    // no reason to use the same kHashCount as fGlyphHash, but we do for now
+    CharGlyphRec    fCharToGlyphHash[kHashCount];
+
+    enum {
+        // shift so that the top bits fall into kHashBits region
+        kShiftForHashIndex = SkGlyph::kSubShift +
+                             SkGlyph::kSubBits*2 -
+                             kHashBits
+    };
+
+    static inline unsigned ID2HashIndex(uint32_t id) {
+        return (id ^ (id >> kShiftForHashIndex)) & kHashMask;
+    }
+
+    // used to track (approx) how much ram is tied-up in this cache
+    size_t  fMemoryUsed;
+
+    struct AuxProcRec {
+        AuxProcRec* fNext;
+        void (*fProc)(void*);
+        void* fData;
+    };
+    AuxProcRec* fAuxProcList;
+    void invokeAndRemoveAuxProcs();
+
+    // This relies on the caller to have already acquired the mutex to access the global cache
+    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;
+};
+
+class SkAutoGlyphCache {
+public:
+    SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {}
+    SkAutoGlyphCache(const SkDescriptor* desc) {
+        fCache = SkGlyphCache::DetachCache(desc);
+    }
+    SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix) {
+        fCache = paint.detachCache(matrix);
+    }
+    ~SkAutoGlyphCache() {
+        if (fCache) {
+            SkGlyphCache::AttachCache(fCache);
+        }
+    }
+
+    SkGlyphCache* getCache() const { return fCache; }
+
+    void release() {
+        if (fCache) {
+            SkGlyphCache::AttachCache(fCache);
+            fCache = NULL;
+        }
+    }
+
+private:
+    SkGlyphCache*   fCache;
+
+    static bool DetachProc(const SkGlyphCache*, void*);
+};
+
+#endif
+
diff --git a/legacy/src/core/SkGraphics.cpp b/legacy/src/core/SkGraphics.cpp
new file mode 100644
index 0000000..ff38f85
--- /dev/null
+++ b/legacy/src/core/SkGraphics.cpp
@@ -0,0 +1,211 @@
+
+/*
+ * 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 "SkGraphics.h"
+
+#include "Sk64.h"
+#include "SkBlitter.h"
+#include "SkCanvas.h"
+#include "SkFloat.h"
+#include "SkGeometry.h"
+#include "SkMath.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPathEffect.h"
+#include "SkPixelRef.h"
+#include "SkRandom.h"
+#include "SkRefCnt.h"
+#include "SkScalerContext.h"
+#include "SkShader.h"
+#include "SkStream.h"
+#include "SkTSearch.h"
+#include "SkTime.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+void SkGraphics::GetVersion(int32_t* major, int32_t* minor, int32_t* patch) {
+    if (major) {
+        *major = SKIA_VERSION_MAJOR;
+    }
+    if (minor) {
+        *minor = SKIA_VERSION_MINOR;
+    }
+    if (patch) {
+        *patch = SKIA_VERSION_PATCH;
+    }
+}
+
+#define typesizeline(type)  { #type , sizeof(type) }
+
+#ifdef BUILD_EMBOSS_TABLE
+    extern void SkEmbossMask_BuildTable();
+#endif
+
+#ifdef BUILD_RADIALGRADIENT_TABLE
+    extern void SkRadialGradient_BuildTable();
+#endif
+
+void SkGraphics::Init() {
+#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+    SkFlattenable::InitializeFlattenables();
+    SkPixelRef::InitializeFlattenables();
+#endif
+#ifdef BUILD_EMBOSS_TABLE
+    SkEmbossMask_BuildTable();
+#endif
+#ifdef BUILD_RADIALGRADIENT_TABLE
+    SkRadialGradient_BuildTable();
+#endif
+
+#ifdef SK_DEBUGx
+    int i;
+
+    static const struct {
+        const char* fTypeName;
+        size_t      fSizeOf;
+    } gTypeSize[] = {
+        typesizeline(char),
+        typesizeline(short),
+        typesizeline(int),
+        typesizeline(long),
+        typesizeline(size_t),
+        typesizeline(void*),
+
+        typesizeline(S8CPU),
+        typesizeline(U8CPU),
+        typesizeline(S16CPU),
+        typesizeline(U16CPU),
+
+        typesizeline(SkPoint),
+        typesizeline(SkRect),
+        typesizeline(SkMatrix),
+        typesizeline(SkPath),
+        typesizeline(SkGlyph),
+        typesizeline(SkRefCnt),
+
+        typesizeline(SkPaint),
+        typesizeline(SkCanvas),
+        typesizeline(SkBlitter),
+        typesizeline(SkShader),
+        typesizeline(SkXfermode),
+        typesizeline(SkPathEffect)
+    };
+
+#ifdef SK_CPU_BENDIAN
+    SkDebugf("SkGraphics: big-endian\n");
+#else
+    SkDebugf("SkGraphics: little-endian\n");
+#endif
+
+    {
+        char    test = 0xFF;
+        int     itest = test;   // promote to int, see if it sign-extended
+        if (itest < 0)
+            SkDebugf("SkGraphics: char is signed\n");
+        else
+            SkDebugf("SkGraphics: char is unsigned\n");
+    }
+    for (i = 0; i < (int)SK_ARRAY_COUNT(gTypeSize); i++) {
+        SkDebugf("SkGraphics: sizeof(%s) = %d\n",
+                 gTypeSize[i].fTypeName, gTypeSize[i].fSizeOf);
+    }
+    SkDebugf("SkGraphics: font cache limit %dK\n",
+             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();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char kFontCacheLimitStr[] = "font-cache-limit";
+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}
+};
+
+/* flags are of the form param; or param=value; */
+void SkGraphics::SetFlags(const char* flags) {
+    if (!flags) {
+        return;
+    }
+    const char* nextSemi;
+    do {
+        size_t len = strlen(flags);
+        const char* paramEnd = flags + len;
+        const char* nextEqual = strchr(flags, '=');
+        if (nextEqual && paramEnd > nextEqual) {
+            paramEnd = nextEqual;
+        }
+        nextSemi = strchr(flags, ';');
+        if (nextSemi && paramEnd > nextSemi) {
+            paramEnd = nextSemi;
+        }
+        size_t paramLen = paramEnd - flags;
+        for (int i = 0; i < (int)SK_ARRAY_COUNT(gFlags); ++i) {
+            if (paramLen != gFlags[i].fLen) {
+                continue;
+            }
+            if (strncmp(flags, gFlags[i].fStr, paramLen) == 0) {
+                size_t val = 0;
+                if (nextEqual) {
+                    val = (size_t) atoi(nextEqual + 1);
+                }
+                (gFlags[i].fFunc)(val);
+                break;
+            }
+        }
+        flags = nextSemi + 1;
+    } while (nextSemi);
+}
diff --git a/legacy/src/core/SkLanguage.cpp b/legacy/src/core/SkLanguage.cpp
new file mode 100644
index 0000000..2d0bcd5
--- /dev/null
+++ b/legacy/src/core/SkLanguage.cpp
@@ -0,0 +1,53 @@
+
+/*
+ * 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 "SkLanguage.h"
+
+#ifdef SK_BUILD_FOR_ANDROID // currently only for Android
+
+#include "SkTDict.h"
+#include "SkThread.h"
+#include <cstring>
+
+SkLanguage SkLanguage::getParent() const {
+    SkASSERT(fInfo != NULL);
+    const char* tag = fInfo->fTag.c_str();
+    SkASSERT(tag != NULL);
+
+    // strip off the rightmost "-.*"
+    char* parentTagEnd = strrchr(tag, '-');
+    if (parentTagEnd == NULL) {
+        return SkLanguage("");
+    }
+    size_t parentTagLen = parentTagEnd - tag;
+    char parentTag[parentTagLen + 1];
+    strncpy(parentTag, tag, parentTagLen);
+    parentTag[parentTagLen] = '\0';
+    return SkLanguage(parentTag);
+}
+
+SK_DECLARE_STATIC_MUTEX(gGetInfoMutex);
+const SkLanguageInfo* SkLanguage::getInfo(const char* tag) {
+    SkAutoMutexAcquire lock(gGetInfoMutex);
+
+    static const size_t kDictSize = 128;
+    static SkTDict<SkLanguageInfo*> tagToInfo(kDictSize);
+
+    // try a lookup
+    SkLanguageInfo* info;
+    if (tagToInfo.find(tag, &info)) {
+        return info;
+    }
+
+    // no match - add this language
+    info = new SkLanguageInfo(tag);
+    tagToInfo.set(tag, info);
+    return info;
+}
+
+#endif
diff --git a/legacy/src/core/SkLineClipper.cpp b/legacy/src/core/SkLineClipper.cpp
new file mode 100644
index 0000000..708d3a9
--- /dev/null
+++ b/legacy/src/core/SkLineClipper.cpp
@@ -0,0 +1,250 @@
+
+/*
+ * 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 "SkLineClipper.h"
+
+// 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;
+    if (SkScalarNearlyZero(dy)) {
+        return SkScalarAve(src[0].fX, src[1].fX);
+    } else {
+#ifdef SK_SCALAR_IS_FLOAT
+        // need the extra precision so we don't compute a value that exceeds
+        // our original limits
+        double X0 = src[0].fX;
+        double Y0 = src[0].fY;
+        double X1 = src[1].fX;
+        double Y1 = src[1].fY;
+        double result = X0 + ((double)Y - Y0) * (X1 - X0) / (Y1 - Y0);
+        return (float)result;
+#else
+        return src[0].fX + SkScalarMulDiv(Y - src[0].fY, src[1].fX - src[0].fX,
+                                          dy);
+#endif
+    }
+}
+
+// return Y coordinate of intersection with vertical line at X
+static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) {
+    SkScalar dx = src[1].fX - src[0].fX;
+    if (SkScalarNearlyZero(dx)) {
+        return SkScalarAve(src[0].fY, src[1].fY);
+    } else {
+#ifdef SK_SCALAR_IS_FLOAT
+        // need the extra precision so we don't compute a value that exceeds
+        // our original limits
+        double X0 = src[0].fX;
+        double Y0 = src[0].fY;
+        double X1 = src[1].fX;
+        double Y1 = src[1].fY;
+        double result = Y0 + ((double)X - X0) * (Y1 - Y0) / (X1 - X0);
+        return (float)result;
+#else
+        return src[0].fY + SkScalarMulDiv(X - src[0].fX, src[1].fY - src[0].fY,
+                                          dx);
+#endif
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline bool nestedLT(SkScalar a, SkScalar b, SkScalar dim) {
+    return a <= b && (a < b || dim > 0);
+}
+
+// returns true if outer contains inner, even if inner is empty.
+// note: outer.contains(inner) always returns false if inner is empty.
+static inline bool containsNoEmptyCheck(const SkRect& outer,
+                                        const SkRect& inner) {
+    return  outer.fLeft <= inner.fLeft && outer.fTop <= inner.fTop &&
+            outer.fRight >= inner.fRight && outer.fBottom >= inner.fBottom;
+}
+
+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) {
+            memcpy(dst, src, 2 * sizeof(SkPoint));
+        }
+        return true;
+    }
+    // check for no overlap, and only permit coincident edges if the line
+    // and the edge are colinear
+    if (nestedLT(bounds.fRight, clip.fLeft, bounds.width()) ||
+        nestedLT(clip.fRight, bounds.fLeft, bounds.width()) ||
+        nestedLT(bounds.fBottom, clip.fTop, bounds.height()) ||
+        nestedLT(clip.fBottom, bounds.fTop, bounds.height())) {
+        return false;
+    }
+
+    int index0, index1;
+    
+    if (src[0].fY < src[1].fY) {
+        index0 = 0;
+        index1 = 1;
+    } else {
+        index0 = 1;
+        index1 = 0;
+    }
+
+    SkPoint tmp[2];
+    memcpy(tmp, src, sizeof(tmp));
+
+    // now compute Y intersections
+    if (tmp[index0].fY < clip.fTop) {
+        tmp[index0].set(sect_with_horizontal(src, clip.fTop), clip.fTop);
+    }
+    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;
+    } else {
+        index0 = 1;
+        index1 = 0;
+    }
+
+    // check for quick-reject in X again, now that we may have been chopped
+    if ((tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight) &&
+        tmp[index0].fX < tmp[index1].fX) {
+        // only reject if we have a non-zero width
+        return false;
+    }
+
+    if (tmp[index0].fX < clip.fLeft) {
+        tmp[index0].set(clip.fLeft, sect_with_vertical(src, clip.fLeft));
+    }
+    if (tmp[index1].fX > clip.fRight) {
+        tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight));
+    }
+#ifdef SK_DEBUG
+    bounds.set(tmp, 2);
+    SkASSERT(containsNoEmptyCheck(clip, bounds));
+#endif
+    memcpy(dst, tmp, sizeof(tmp));
+    return true;
+}
+
+#ifdef SK_DEBUG
+// return value between the two limits, where the limits are either ascending
+// or descending.
+static bool is_between_unsorted(SkScalar value,
+                                SkScalar limit0, SkScalar limit1) {
+    if (limit0 < limit1) {
+        return limit0 <= value && value <= limit1;
+    } else {
+        return limit1 <= value && value <= limit0;
+    }
+}
+#endif
+
+int SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip,
+                            SkPoint lines[]) {
+    int index0, index1;
+
+    if (pts[0].fY < pts[1].fY) {
+        index0 = 0;
+        index1 = 1;
+    } else {
+        index0 = 1;
+        index1 = 0;
+    }
+
+    // Check if we're completely clipped out in Y (above or below
+
+    if (pts[index1].fY <= clip.fTop) {  // we're above the clip
+        return 0;
+    }
+    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];
+    memcpy(tmp, pts, sizeof(tmp));
+
+    // now compute intersections
+    if (pts[index0].fY < clip.fTop) {
+        tmp[index0].set(sect_with_horizontal(pts, clip.fTop), clip.fTop);
+        SkASSERT(is_between_unsorted(tmp[index0].fX, pts[0].fX, pts[1].fX));
+    }
+    if (tmp[index1].fY > clip.fBottom) {
+        tmp[index1].set(sect_with_horizontal(pts, clip.fBottom), clip.fBottom);
+        SkASSERT(is_between_unsorted(tmp[index1].fX, pts[0].fX, pts[1].fX));
+    }
+
+    // Chop it into 1..3 segments that are wholly within the clip in X.
+
+    // temp storage for up to 3 segments
+    SkPoint resultStorage[kMaxPoints];
+    SkPoint* result;    // points to our results, either tmp or resultStorage
+    int lineCount = 1;
+    bool reverse;
+
+    if (pts[0].fX < pts[1].fX) {
+        index0 = 0;
+        index1 = 1;
+        reverse = false;
+    } else {
+        index0 = 1;
+        index1 = 0;
+        reverse = true;
+    }
+
+    if (tmp[index1].fX <= clip.fLeft) {  // wholly to the left
+        tmp[0].fX = tmp[1].fX = clip.fLeft;
+        result = tmp;
+        reverse = false;
+    } else if (tmp[index0].fX >= clip.fRight) {    // wholly to the right
+        tmp[0].fX = tmp[1].fX = clip.fRight;
+        result = tmp;
+        reverse = false;
+    } else {
+        result = resultStorage;
+        SkPoint* r = result;
+        
+        if (tmp[index0].fX < clip.fLeft) {
+            r->set(clip.fLeft, tmp[index0].fY);
+            r += 1;
+            r->set(clip.fLeft, sect_with_vertical(tmp, clip.fLeft));
+            SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY));
+        } else {
+            *r = tmp[index0];
+        }
+        r += 1;
+
+        if (tmp[index1].fX > clip.fRight) {
+            r->set(clip.fRight, sect_with_vertical(tmp, clip.fRight));
+            SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY));
+            r += 1;
+            r->set(clip.fRight, tmp[index1].fY);
+        } else {
+            *r = tmp[index1];
+        }
+
+        lineCount = r - result;
+    }
+
+    // Now copy the results into the caller's lines[] parameter
+    if (reverse) {
+        // copy the pts in reverse order to maintain winding order
+        for (int i = 0; i <= lineCount; i++) {
+            lines[lineCount - i] = result[i];
+        }
+    } else {
+        memcpy(lines, result, (lineCount + 1) * sizeof(SkPoint));
+    }
+    return lineCount;
+}
+
diff --git a/legacy/src/core/SkMMapStream.cpp b/legacy/src/core/SkMMapStream.cpp
new file mode 100644
index 0000000..0aec5e1
--- /dev/null
+++ b/legacy/src/core/SkMMapStream.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 "SkMMapStream.h"
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+SkMMAPStream::SkMMAPStream(const char filename[])
+{
+    fAddr = NULL;   // initialize to failure case
+
+    int fildes = open(filename, O_RDONLY);
+    if (fildes < 0)
+    {
+        SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno));
+        return;
+    }
+
+    off_t offset = lseek(fildes, 0, SEEK_END);    // find the file size
+    if (offset == -1)
+    {
+        SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno));
+        close(fildes);
+        return;
+    }
+    (void)lseek(fildes, 0, SEEK_SET);   // restore file offset to beginning
+
+    // to avoid a 64bit->32bit warning, I explicitly create a size_t size
+    size_t size = static_cast<size_t>(offset);
+
+    void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0);
+
+    // According to the POSIX documentation of mmap it adds an extra reference
+    // to the file associated with the fildes which is not removed by a
+    // subsequent close() on that fildes. This reference is removed when there
+    // are no more mappings to the file.
+    close(fildes);
+
+    if (MAP_FAILED == addr)
+    {
+        SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno));
+        return;
+    }
+
+    this->INHERITED::setMemory(addr, size);
+
+    fAddr = addr;
+    fSize = size;
+}
+
+SkMMAPStream::~SkMMAPStream()
+{
+    this->closeMMap();
+}
+
+void SkMMAPStream::setMemory(const void* data, size_t length, bool copyData)
+{
+    this->closeMMap();
+    this->INHERITED::setMemory(data, length, copyData);
+}
+
+void SkMMAPStream::closeMMap()
+{
+    if (fAddr)
+    {
+        munmap(fAddr, fSize);
+        fAddr = NULL;
+    }
+}
+
diff --git a/legacy/src/core/SkMallocPixelRef.cpp b/legacy/src/core/SkMallocPixelRef.cpp
new file mode 100644
index 0000000..72dbb3d
--- /dev/null
+++ b/legacy/src/core/SkMallocPixelRef.cpp
@@ -0,0 +1,66 @@
+
+/*
+ * 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 "SkMallocPixelRef.h"
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+
+SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
+                                   SkColorTable* ctable) {
+    if (NULL == storage) {
+        storage = sk_malloc_throw(size);
+    }
+    fStorage = storage;
+    fSize = size;
+    fCTable = ctable;
+    SkSafeRef(ctable);
+    
+    this->setPreLocked(fStorage, fCTable);
+}
+
+SkMallocPixelRef::~SkMallocPixelRef() {
+    SkSafeUnref(fCTable);
+    sk_free(fStorage);
+}
+
+void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
+    *ct = fCTable;
+    return fStorage;
+}
+
+void SkMallocPixelRef::onUnlockPixels() {
+    // nothing to do
+}
+
+void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.write32(fSize);
+    buffer.writePad(fStorage, fSize);
+    if (fCTable) {
+        buffer.writeBool(true);
+        fCTable->flatten(buffer);
+    } else {
+        buffer.writeBool(false);
+    }
+}
+
+SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, NULL) {
+    fSize = buffer.readU32();
+    fStorage = sk_malloc_throw(fSize);
+    buffer.read(fStorage, fSize);
+    if (buffer.readBool()) {
+        fCTable = SkNEW_ARGS(SkColorTable, (buffer));
+    } else {
+        fCTable = NULL;
+    }
+
+    this->setPreLocked(fStorage, fCTable);
+}
+
+SK_DEFINE_PIXEL_REF_REGISTRAR(SkMallocPixelRef)
diff --git a/legacy/src/core/SkMask.cpp b/legacy/src/core/SkMask.cpp
new file mode 100644
index 0000000..36047fe
--- /dev/null
+++ b/legacy/src/core/SkMask.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2007 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 "Sk64.h"
+#include "SkMask.h"
+
+/** returns the product if it is positive and fits in 31 bits. Otherwise this
+    returns 0.
+ */
+static int32_t safeMul32(int32_t a, int32_t b) {
+    Sk64 size;
+    size.setMul(a, b);
+    if (size.is32() && size.isPos()) {
+        return size.get32();
+    }
+    return 0;
+}
+
+size_t SkMask::computeImageSize() const {
+    return safeMul32(fBounds.height(), fRowBytes);
+}
+
+size_t SkMask::computeTotalImageSize() const {
+    size_t size = this->computeImageSize();
+    if (fFormat == SkMask::k3D_Format) {
+        size = safeMul32(size, 3);
+    }
+    return size;
+}
+
+/** We explicitly use this allocator for SkBimap pixels, so that we can
+    freely assign memory allocated by one class to the other.
+*/
+uint8_t* SkMask::AllocImage(size_t size) {
+    return (uint8_t*)sk_malloc_throw(SkAlign4(size));
+}
+
+/** We explicitly use this allocator for SkBimap pixels, so that we can
+    freely assign memory allocated by one class to the other.
+*/
+void SkMask::FreeImage(void* image) {
+    sk_free(image);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const int gMaskFormatToShift[] = {
+    ~0, // BW -- not supported
+    0,  // A8
+    0,  // 3D
+    2,  // ARGB32
+    1,  // LCD16
+    2   // LCD32
+};
+
+static int maskFormatToShift(SkMask::Format format) {
+    SkASSERT((unsigned)format < SK_ARRAY_COUNT(gMaskFormatToShift));
+    SkASSERT(SkMask::kBW_Format != format);
+    return gMaskFormatToShift[format];
+}
+
+void* SkMask::getAddr(int x, int y) const {
+    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/legacy/src/core/SkMaskFilter.cpp b/legacy/src/core/SkMaskFilter.cpp
new file mode 100644
index 0000000..42d07a6
--- /dev/null
+++ b/legacy/src/core/SkMaskFilter.cpp
@@ -0,0 +1,75 @@
+
+/*
+ * 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 "SkMaskFilter.h"
+#include "SkBlitter.h"
+#include "SkBounder.h"
+#include "SkBuffer.h"
+#include "SkDraw.h"
+#include "SkRasterClip.h"
+
+bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
+                              SkIPoint*) {
+    return false;
+}
+
+bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
+                              const SkRasterClip& clip, SkBounder* bounder,
+                              SkBlitter* blitter) {
+    SkMask  srcM, dstM;
+
+    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
+        return false;
+    }
+    SkAutoMaskFreeImage autoSrc(srcM.fImage);
+
+    if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
+        return false;
+    }
+    SkAutoMaskFreeImage autoDst(dstM.fImage);
+
+    // 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(), dstM.fBounds);
+
+    if (!clipper.done() && (bounder == NULL || bounder->doIRect(dstM.fBounds))) {
+        const SkIRect& cr = clipper.rect();
+        do {
+            blitter->blitMask(dstM, cr);
+            clipper.next();
+        } while (!clipper.done());
+    }
+
+    return true;
+}
+
+SkMaskFilter::BlurType SkMaskFilter::asABlur(BlurInfo*) const {
+    return kNone_BlurType;
+}
+
+void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) {
+    SkMask  srcM, dstM;
+
+    srcM.fImage = NULL;
+    src.roundOut(&srcM.fBounds);
+    srcM.fRowBytes = 0;
+    srcM.fFormat = SkMask::kA8_Format;
+
+    SkIPoint margin;    // ignored
+    if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
+        dst->set(dstM.fBounds);
+    } else {
+        dst->set(srcM.fBounds);
+    }
+}
+
+
diff --git a/legacy/src/core/SkMath.cpp b/legacy/src/core/SkMath.cpp
new file mode 100644
index 0000000..7b75305
--- /dev/null
+++ b/legacy/src/core/SkMath.cpp
@@ -0,0 +1,538 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkMath.h"
+#include "SkCordic.h"
+#include "SkFloatBits.h"
+#include "SkFloatingPoint.h"
+#include "Sk64.h"
+#include "SkScalar.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+    const uint32_t gIEEENotANumber = 0x7FFFFFFF;
+    const uint32_t gIEEEInfinity = 0x7F800000;
+#endif
+
+#define sub_shift(zeros, x, n)  \
+    zeros -= n;                 \
+    x >>= n
+    
+int SkCLZ_portable(uint32_t x) {
+    if (x == 0) {
+        return 32;
+    }
+
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    int zeros = 31;
+    if (x & 0xFFFF0000) {
+        sub_shift(zeros, x, 16);
+    }
+    if (x & 0xFF00) {
+        sub_shift(zeros, x, 8);
+    }
+    if (x & 0xF0) {
+        sub_shift(zeros, x, 4);
+    }
+    if (x & 0xC) {
+        sub_shift(zeros, x, 2);
+    }
+    if (x & 0x2) {
+        sub_shift(zeros, x, 1);
+    }
+#else
+    int zeros = ((x >> 16) - 1) >> 31 << 4;
+    x <<= zeros;
+
+    int nonzero = ((x >> 24) - 1) >> 31 << 3;
+    zeros += nonzero;
+    x <<= nonzero;
+
+    nonzero = ((x >> 28) - 1) >> 31 << 2;
+    zeros += nonzero;
+    x <<= nonzero;
+
+    nonzero = ((x >> 30) - 1) >> 31 << 1;
+    zeros += nonzero;
+    x <<= nonzero;
+
+    zeros += (~x) >> 31;
+#endif
+
+    return zeros;
+}
+
+int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom) {
+    SkASSERT(denom);
+
+    Sk64 tmp;
+    tmp.setMul(numer1, numer2);
+    tmp.div(denom, Sk64::kTrunc_DivOption);
+    return tmp.get32();
+}
+
+int32_t SkMulShift(int32_t a, int32_t b, unsigned shift) {
+    int sign = SkExtractSign(a ^ b);
+
+    if (shift > 63) {
+        return sign;
+    }
+
+    a = SkAbs32(a);
+    b = SkAbs32(b);
+
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t A = ah * bh;
+    uint32_t B = ah * bl + al * bh;
+    uint32_t C = al * bl;
+
+    /*  [  A  ]
+           [  B  ]
+              [  C  ]
+    */
+    uint32_t lo = C + (B << 16);
+    int32_t  hi = A + (B >> 16) + (lo < C);
+
+    if (sign < 0) {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+
+    if (shift == 0) {
+#ifdef SK_DEBUGx
+        SkASSERT(((int32_t)lo >> 31) == hi);
+#endif
+        return lo;
+    } else if (shift >= 32) {
+        return hi >> (shift - 32);
+    } else {
+#ifdef SK_DEBUGx
+        int32_t tmp = hi >> shift;
+        SkASSERT(tmp == 0 || tmp == -1);
+#endif
+        // we want (hi << (32 - shift)) | (lo >> shift) but rounded
+        int roundBit = (lo >> (shift - 1)) & 1;
+        return ((hi << (32 - shift)) | (lo >> shift)) + roundBit;
+    }
+}
+
+SkFixed SkFixedMul_portable(SkFixed a, SkFixed b) {
+#if 0
+    Sk64    tmp;
+
+    tmp.setMul(a, b);
+    tmp.shiftRight(16);
+    return tmp.fLo;
+#elif defined(SkLONGLONG)
+    return static_cast<SkFixed>((SkLONGLONG)a * b >> 16);
+#else
+    int sa = SkExtractSign(a);
+    int sb = SkExtractSign(b);
+    // now make them positive
+    a = SkApplySign(a, sa);
+    b = SkApplySign(b, sb);
+
+    uint32_t    ah = a >> 16;
+    uint32_t    al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t R = ah * b + al * bh + (al * bl >> 16);
+
+    return SkApplySign(R, sa ^ sb);
+#endif
+}
+
+SkFract SkFractMul_portable(SkFract a, SkFract b) {
+#if 0
+    Sk64 tmp;
+    tmp.setMul(a, b);
+    return tmp.getFract();
+#elif defined(SkLONGLONG)
+    return static_cast<SkFract>((SkLONGLONG)a * b >> 30);
+#else
+    int sa = SkExtractSign(a);
+    int sb = SkExtractSign(b);
+    // now make them positive
+    a = SkApplySign(a, sa);
+    b = SkApplySign(b, sb);
+
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t A = ah * bh;
+    uint32_t B = ah * bl + al * bh;
+    uint32_t C = al * bl;
+
+    /*  [  A  ]
+           [  B  ]
+              [  C  ]
+    */
+    uint32_t Lo = C + (B << 16);
+    uint32_t Hi = A + (B >>16) + (Lo < C);
+
+    SkASSERT((Hi >> 29) == 0);  // else overflow
+
+    int32_t R = (Hi << 2) + (Lo >> 30);
+
+    return SkApplySign(R, sa ^ sb);
+#endif
+}
+
+int SkFixedMulCommon(SkFixed a, int b, int bias) {
+    // this function only works if b is 16bits
+    SkASSERT(b == (int16_t)b);
+    SkASSERT(b >= 0);
+
+    int sa = SkExtractSign(a);
+    a = SkApplySign(a, sa);
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    uint32_t R = ah * b + ((al * b + bias) >> 16);
+    return SkApplySign(R, sa);
+}
+
+#ifdef SK_DEBUGx
+    #define TEST_FASTINVERT
+#endif
+
+SkFixed SkFixedFastInvert(SkFixed x) {
+/*  Adapted (stolen) from gglRecip()
+*/
+
+    if (x == SK_Fixed1) {
+        return SK_Fixed1;
+    }
+
+    int      sign = SkExtractSign(x);
+    uint32_t a = SkApplySign(x, sign);
+
+    if (a <= 2) {
+        return SkApplySign(SK_MaxS32, sign);
+    }
+
+#ifdef TEST_FASTINVERT
+    SkFixed orig = a;
+    uint32_t slow = SkFixedDiv(SK_Fixed1, a);
+#endif
+
+    // normalize a
+    int lz = SkCLZ(a);
+    a = a << lz >> 16;
+
+    // compute 1/a approximation (0.5 <= a < 1.0) 
+    uint32_t r = 0x17400 - a;      // (2.90625 (~2.914) - 2*a) >> 1
+
+    // Newton-Raphson iteration:
+    // x = r*(2 - a*r) = ((r/2)*(1 - a*r/2))*4
+    r = ( (0x10000 - ((a*r)>>16)) * r ) >> 15;
+    r = ( (0x10000 - ((a*r)>>16)) * r ) >> (30 - lz);
+
+#ifdef TEST_FASTINVERT
+    SkDebugf("SkFixedFastInvert(%x %g) = %x %g Slow[%x %g]\n",
+                orig, orig/65536.,
+                r, r/65536.,
+                slow, slow/65536.);
+#endif
+
+    return SkApplySign(r, sign);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DIVBITS_ITER(n)                                 \
+    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);
+    denom = SkAbs32(denom);
+
+    int nbits = SkCLZ(numer) - 1;
+    int dbits = SkCLZ(denom) - 1;
+    int bits = shift_bias - nbits + dbits;
+
+    if (bits < 0) {  // answer will underflow
+        return 0;
+    }
+    if (bits > 31) {  // answer will overflow
+        return SkApplySign(SK_MaxS32, sign);
+    }
+
+    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
+        result <<= bits;
+        switch (bits) {
+            DIVBITS_ITER(31); DIVBITS_ITER(30); DIVBITS_ITER(29);
+            DIVBITS_ITER(28); DIVBITS_ITER(27); DIVBITS_ITER(26);
+            DIVBITS_ITER(25); DIVBITS_ITER(24); DIVBITS_ITER(23);
+            DIVBITS_ITER(22); DIVBITS_ITER(21); DIVBITS_ITER(20);
+            DIVBITS_ITER(19); DIVBITS_ITER(18); DIVBITS_ITER(17);
+            DIVBITS_ITER(16); DIVBITS_ITER(15); DIVBITS_ITER(14);
+            DIVBITS_ITER(13); DIVBITS_ITER(12); DIVBITS_ITER(11);
+            DIVBITS_ITER(10); DIVBITS_ITER( 9); DIVBITS_ITER( 8);
+            DIVBITS_ITER( 7); DIVBITS_ITER( 6); DIVBITS_ITER( 5);
+            DIVBITS_ITER( 4); DIVBITS_ITER( 3); DIVBITS_ITER( 2);
+            // we merge these last two together, makes GCC make better ARM
+            default:
+            DIVBITS_ITER( 1);
+        }
+    }
+
+    if (result < 0) {
+        result = SK_MaxS32;
+    }
+    return SkApplySign(result, sign);
+}
+
+/*  mod(float numer, float denom) seems to always return the sign
+    of the numer, so that's what we do too
+*/
+SkFixed SkFixedMod(SkFixed numer, SkFixed denom) {
+    int sn = SkExtractSign(numer);
+    int sd = SkExtractSign(denom);
+
+    numer = SkApplySign(numer, sn);
+    denom = SkApplySign(denom, sd);
+    
+    if (numer < denom) {
+        return SkApplySign(numer, sn);
+    } else if (numer == denom) {
+        return 0;
+    } else {
+        SkFixed div = SkFixedDiv(numer, denom);
+        return SkApplySign(SkFixedMul(denom, div & 0xFFFF), sn);
+    }
+}
+
+/* www.worldserver.com/turk/computergraphics/FixedSqrt.pdf
+*/
+int32_t SkSqrtBits(int32_t x, int count) {
+    SkASSERT(x >= 0 && count > 0 && (unsigned)count <= 30);
+
+    uint32_t    root = 0;
+    uint32_t    remHi = 0;
+    uint32_t    remLo = x;
+
+    do {
+        root <<= 1;
+
+        remHi = (remHi<<2) | (remLo>>30);
+        remLo <<= 2;
+
+        uint32_t testDiv = (root << 1) + 1;
+        if (remHi >= testDiv) {
+            remHi -= testDiv;
+            root++;
+        }
+    } while (--count >= 0);
+
+    return root;
+}
+
+int32_t SkCubeRootBits(int32_t value, int bits) {
+    SkASSERT(bits > 0);
+
+    int sign = SkExtractSign(value);
+    value = SkApplySign(value, sign);
+
+    uint32_t root = 0;
+    uint32_t curr = (uint32_t)value >> 30;
+    value <<= 2;
+
+    do {
+        root <<= 1;
+        uint32_t guess = root * root + root;
+        guess = (guess << 1) + guess;   // guess *= 3
+        if (guess < curr) {
+            curr -= guess + 1;
+            root |= 1;
+        }
+        curr = (curr << 3) | ((uint32_t)value >> 29);
+        value <<= 3;
+    } while (--bits);
+
+    return SkApplySign(root, sign);
+}
+
+SkFixed SkFixedMean(SkFixed a, SkFixed b) {
+    Sk64 tmp;
+    
+    tmp.setMul(a, b);
+    return tmp.getSqrt();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+float SkScalarSinCos(float radians, float* cosValue) {
+    float sinValue = sk_float_sin(radians);
+
+    if (cosValue) {
+        *cosValue = sk_float_cos(radians);
+        if (SkScalarNearlyZero(*cosValue)) {
+            *cosValue = 0;
+        }
+    }
+
+    if (SkScalarNearlyZero(sinValue)) {
+        sinValue = 0;
+    }
+    return sinValue;
+}
+#endif
+
+#define INTERP_SINTABLE
+#define BUILD_TABLE_AT_RUNTIMEx
+
+#define kTableSize  256
+
+#ifdef BUILD_TABLE_AT_RUNTIME
+    static uint16_t gSkSinTable[kTableSize];
+
+    static void build_sintable(uint16_t table[]) {
+        for (int i = 0; i < kTableSize; i++) {
+            double  rad = i * 3.141592653589793 / (2*kTableSize);
+            double  val = sin(rad);
+            int     ival = (int)(val * SK_Fixed1);
+            table[i] = SkToU16(ival);
+        }
+    }
+#else
+    #include "SkSinTable.h"
+#endif
+
+#define SK_Fract1024SizeOver2PI     0x28BE60    /* floatToFract(1024 / 2PI) */
+
+#ifdef INTERP_SINTABLE
+static SkFixed interp_table(const uint16_t table[], int index, int partial255) {
+    SkASSERT((unsigned)index < kTableSize);
+    SkASSERT((unsigned)partial255 <= 255);
+
+    SkFixed lower = table[index];
+    SkFixed upper = (index == kTableSize - 1) ? SK_Fixed1 : table[index + 1];
+
+    SkASSERT(lower < upper);
+    SkASSERT(lower >= 0);
+    SkASSERT(upper <= SK_Fixed1);
+
+    partial255 += (partial255 >> 7);
+    return lower + ((upper - lower) * partial255 >> 8);
+}
+#endif
+
+SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValuePtr) {
+    SkASSERT(SK_ARRAY_COUNT(gSkSinTable) == kTableSize);
+
+#ifdef BUILD_TABLE_AT_RUNTIME
+    static bool gFirstTime = true;
+    if (gFirstTime) {
+        build_sintable(gSinTable);
+        gFirstTime = false;
+    }
+#endif
+
+    // make radians positive
+    SkFixed sinValue, cosValue;
+    int32_t cosSign = 0;
+    int32_t sinSign = SkExtractSign(radians);
+    radians = SkApplySign(radians, sinSign);
+    // scale it to 0...1023 ...
+
+#ifdef INTERP_SINTABLE
+    radians = SkMulDiv(radians, 2 * kTableSize * 256, SK_FixedPI);
+    int findex = radians & (kTableSize * 256 - 1);
+    int index = findex >> 8;
+    int partial = findex & 255;
+    sinValue = interp_table(gSkSinTable, index, partial);
+
+    findex = kTableSize * 256 - findex - 1;
+    index = findex >> 8;
+    partial = findex & 255;
+    cosValue = interp_table(gSkSinTable, index, partial);
+
+    int quad = ((unsigned)radians / (kTableSize * 256)) & 3;
+#else
+    radians = SkMulDiv(radians, 2 * kTableSize, SK_FixedPI);
+    int     index = radians & (kTableSize - 1);
+
+    if (index == 0) {
+        sinValue = 0;
+        cosValue = SK_Fixed1;
+    } else {
+        sinValue = gSkSinTable[index];
+        cosValue = gSkSinTable[kTableSize - index];
+    }
+    int quad = ((unsigned)radians / kTableSize) & 3;
+#endif
+
+    if (quad & 1) {
+        SkTSwap<SkFixed>(sinValue, cosValue);
+    }
+    if (quad & 2) {
+        sinSign = ~sinSign;
+    }
+    if (((quad - 1) & 2) == 0) {
+        cosSign = ~cosSign;
+    }
+
+    // restore the sign for negative angles
+    sinValue = SkApplySign(sinValue, sinSign);
+    cosValue = SkApplySign(cosValue, cosSign);
+
+#ifdef SK_DEBUG
+    if (1) {
+        SkFixed sin2 = SkFixedMul(sinValue, sinValue);
+        SkFixed cos2 = SkFixedMul(cosValue, cosValue);
+        int diff = cos2 + sin2 - SK_Fixed1;
+        SkASSERT(SkAbs32(diff) <= 7);
+    }
+#endif
+
+    if (cosValuePtr) {
+        *cosValuePtr = cosValue;
+    }
+    return sinValue;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFixed SkFixedTan(SkFixed radians) { return SkCordicTan(radians); }
+SkFixed SkFixedASin(SkFixed x) { return SkCordicASin(x); }
+SkFixed SkFixedACos(SkFixed x) { return SkCordicACos(x); }
+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/legacy/src/core/SkMatrix.cpp b/legacy/src/core/SkMatrix.cpp
new file mode 100644
index 0000000..6e0bf02
--- /dev/null
+++ b/legacy/src/core/SkMatrix.cpp
@@ -0,0 +1,1778 @@
+
+/*
+ * 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 "SkMatrix.h"
+#include "Sk64.h"
+#include "SkFloatBits.h"
+#include "SkScalarCompare.h"
+#include "SkString.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define kMatrix22Elem   SK_Scalar1
+
+    static inline float SkDoubleToFloat(double x) {
+        return static_cast<float>(x);
+    }
+#else
+    #define kMatrix22Elem   SK_Fract1
+#endif
+
+/*      [scale-x    skew-x      trans-x]   [X]   [X']
+        [skew-y     scale-y     trans-y] * [Y] = [Y']
+        [persp-0    persp-1     persp-2]   [1]   [1 ]
+*/
+
+void SkMatrix::reset() {
+    fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
+    fMat[kMSkewX]  = fMat[kMSkewY] = 
+    fMat[kMTransX] = fMat[kMTransY] =
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
+}
+
+// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
+enum {
+    kTranslate_Shift,
+    kScale_Shift,
+    kAffine_Shift,
+    kPerspective_Shift,
+    kRectStaysRect_Shift
+};
+
+#ifdef SK_SCALAR_IS_FLOAT
+    static const int32_t kScalar1Int = 0x3f800000;
+    static const int32_t kPersp1Int  = 0x3f800000;
+#else
+    #define scalarAsInt(x)  (x)
+    static const int32_t kScalar1Int = (1 << 16);
+    static const int32_t kPersp1Int  = (1 << 30);
+#endif
+
+uint8_t SkMatrix::computePerspectiveTypeMask() const {
+    unsigned mask = kOnlyPerspectiveValid_Mask | kUnknown_Mask;
+
+    if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
+            SkScalarAs2sCompliment(fMat[kMPersp1]) |
+            (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
+        mask |= kPerspective_Mask;
+    }
+
+    return SkToU8(mask);
+}
+
+uint8_t SkMatrix::computeTypeMask() const {
+    unsigned mask = 0;
+
+#ifdef SK_SCALAR_SLOW_COMPARES
+    if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
+            SkScalarAs2sCompliment(fMat[kMPersp1]) |
+            (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
+        mask |= kPerspective_Mask;
+    }
+
+    if (SkScalarAs2sCompliment(fMat[kMTransX]) |
+            SkScalarAs2sCompliment(fMat[kMTransY])) {
+        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;
+    }
+
+    if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
+        mask |= kTranslate_Mask;
+    }
+#endif
+
+    int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
+    int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
+    int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
+    int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
+
+    if (m01 | m10) {
+        mask |= kAffine_Mask;
+    }
+
+    if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
+        mask |= kScale_Mask;
+    }
+
+    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 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;
+    }
+
+    return SkToU8(mask);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+bool operator==(const SkMatrix& a, const SkMatrix& b) {
+    const SkScalar* SK_RESTRICT ma = a.fMat;
+    const SkScalar* SK_RESTRICT mb = b.fMat;
+
+    return  ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
+            ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
+            ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+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[kMPersp0] = fMat[kMPersp1] = 0;
+        fMat[kMPersp2] = kMatrix22Elem;
+
+        this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
+    } else {
+        this->reset();
+    }
+}
+
+bool SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
+    if (this->hasPerspective()) {
+        SkMatrix    m;
+        m.setTranslate(dx, dy);
+        return this->preConcat(m);
+    }
+    
+    if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
+        fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) +
+                          SkScalarMul(fMat[kMSkewX], dy);
+        fMat[kMTransY] += SkScalarMul(fMat[kMSkewY], dx) +
+                          SkScalarMul(fMat[kMScaleY], dy);
+
+        this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+    }
+    return true;
+}
+
+bool SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
+    if (this->hasPerspective()) {
+        SkMatrix    m;
+        m.setTranslate(dx, dy);
+        return this->postConcat(m);
+    }
+    
+    if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
+        fMat[kMTransX] += dx;
+        fMat[kMTransY] += dy;
+        this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
+        this->reset();
+    } else {
+        fMat[kMScaleX] = sx;
+        fMat[kMScaleY] = sy;
+        fMat[kMTransX] = px - SkScalarMul(sx, px);
+        fMat[kMTransY] = py - SkScalarMul(sy, py);
+        fMat[kMPersp2] = kMatrix22Elem;
+
+        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMPersp0] = fMat[kMPersp1] = 0;
+        
+        this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
+    }
+}
+
+void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
+    if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
+        this->reset();
+    } else {
+        fMat[kMScaleX] = sx;
+        fMat[kMScaleY] = sy;
+        fMat[kMPersp2] = kMatrix22Elem;
+
+        fMat[kMTransX] = fMat[kMTransY] =
+        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMPersp0] = fMat[kMPersp1] = 0;
+
+        this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
+    }
+}
+
+bool SkMatrix::setIDiv(int divx, int divy) {
+    if (!divx || !divy) {
+        return false;
+    }
+    this->setScale(SK_Scalar1 / divx, SK_Scalar1 / divy);
+    return true;
+}
+
+bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setScale(sx, sy, px, py);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::preScale(SkScalar sx, SkScalar sy) {
+    if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
+        return true;
+    }
+
+#ifdef SK_SCALAR_IS_FIXED
+    SkMatrix    m;
+    m.setScale(sx, sy);
+    return this->preConcat(m);
+#else
+    // the assumption is that these multiplies are very cheap, and that
+    // a full concat and/or just computing the matrix type is more expensive.
+    // Also, the fixed-point case checks for overflow, but the float doesn't,
+    // so we can get away with these blind multiplies.
+
+    fMat[kMScaleX] = SkScalarMul(fMat[kMScaleX], sx);
+    fMat[kMSkewY] = SkScalarMul(fMat[kMSkewY],   sx);
+    fMat[kMPersp0] = SkScalarMul(fMat[kMPersp0], sx);
+
+    fMat[kMSkewX] = SkScalarMul(fMat[kMSkewX],   sy);
+    fMat[kMScaleY] = SkScalarMul(fMat[kMScaleY], sy);
+    fMat[kMPersp1] = SkScalarMul(fMat[kMPersp1], sy);
+
+    this->orTypeMask(kScale_Mask);
+    return true;
+#endif
+}
+
+bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
+        return true;
+    }
+    SkMatrix    m;
+    m.setScale(sx, sy, px, py);
+    return this->postConcat(m);
+}
+
+bool SkMatrix::postScale(SkScalar sx, SkScalar sy) {
+    if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
+        return true;
+    }
+    SkMatrix    m;
+    m.setScale(sx, sy);
+    return this->postConcat(m);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+    static inline SkFixed roundidiv(SkFixed numer, int denom) {
+        int ns = numer >> 31;
+        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;
+    }
+#endif
+
+// this guy perhaps can go away, if we have a fract/high-precision way to
+// scale matrices
+bool SkMatrix::postIDiv(int divx, int divy) {
+    if (divx == 0 || divy == 0) {
+        return false;
+    }
+
+#ifdef SK_SCALAR_IS_FIXED
+    fMat[kMScaleX] = roundidiv(fMat[kMScaleX], divx);
+    fMat[kMSkewX]  = roundidiv(fMat[kMSkewX],  divx);
+    fMat[kMTransX] = roundidiv(fMat[kMTransX], divx);
+
+    fMat[kMScaleY] = roundidiv(fMat[kMScaleY], divy);
+    fMat[kMSkewY]  = roundidiv(fMat[kMSkewY],  divy);
+    fMat[kMTransY] = roundidiv(fMat[kMTransY], divy);
+#else
+    const float invX = 1.f / divx;
+    const float invY = 1.f / divy;
+
+    fMat[kMScaleX] *= invX;
+    fMat[kMSkewX]  *= invX;
+    fMat[kMTransX] *= invX;
+    
+    fMat[kMScaleY] *= invY;
+    fMat[kMSkewY]  *= invY;
+    fMat[kMTransY] *= invY;
+#endif
+
+    this->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV,
+                         SkScalar px, SkScalar py) {
+    const SkScalar oneMinusCosV = SK_Scalar1 - cosV;
+
+    fMat[kMScaleX]  = cosV;
+    fMat[kMSkewX]   = -sinV;
+    fMat[kMTransX]  = SkScalarMul(sinV, py) + SkScalarMul(oneMinusCosV, px);
+
+    fMat[kMSkewY]   = sinV;
+    fMat[kMScaleY]  = cosV;
+    fMat[kMTransY]  = SkScalarMul(-sinV, px) + SkScalarMul(oneMinusCosV, py);
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+    
+    this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+}
+
+void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
+    fMat[kMScaleX]  = cosV;
+    fMat[kMSkewX]   = -sinV;
+    fMat[kMTransX]  = 0;
+
+    fMat[kMSkewY]   = sinV;
+    fMat[kMScaleY]  = cosV;
+    fMat[kMTransY]  = 0;
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+}
+
+void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+    SkScalar sinV, cosV;
+    sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
+    this->setSinCos(sinV, cosV, px, py);
+}
+
+void SkMatrix::setRotate(SkScalar degrees) {
+    SkScalar sinV, cosV;
+    sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
+    this->setSinCos(sinV, cosV);
+}
+
+bool SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setRotate(degrees, px, py);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::preRotate(SkScalar degrees) {
+    SkMatrix    m;
+    m.setRotate(degrees);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setRotate(degrees, px, py);
+    return this->postConcat(m);
+}
+
+bool SkMatrix::postRotate(SkScalar degrees) {
+    SkMatrix    m;
+    m.setRotate(degrees);
+    return this->postConcat(m);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    fMat[kMScaleX]  = SK_Scalar1;
+    fMat[kMSkewX]   = sx;
+    fMat[kMTransX]  = SkScalarMul(-sx, py);
+
+    fMat[kMSkewY]   = sy;
+    fMat[kMScaleY]  = SK_Scalar1;
+    fMat[kMTransY]  = SkScalarMul(-sy, px);
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+}
+
+void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
+    fMat[kMScaleX]  = SK_Scalar1;
+    fMat[kMSkewX]   = sx;
+    fMat[kMTransX]  = 0;
+
+    fMat[kMSkewY]   = sy;
+    fMat[kMScaleY]  = SK_Scalar1;
+    fMat[kMTransY]  = 0;
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+}
+
+bool SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setSkew(sx, sy, px, py);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
+    SkMatrix    m;
+    m.setSkew(sx, sy);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setSkew(sx, sy, px, py);
+    return this->postConcat(m);
+}
+
+bool SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
+    SkMatrix    m;
+    m.setSkew(sx, sy);
+    return this->postConcat(m);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst,
+                             ScaleToFit align)
+{
+    if (src.isEmpty()) {
+        this->reset();
+        return false;
+    }
+
+    if (dst.isEmpty()) {
+        sk_bzero(fMat, 8 * sizeof(SkScalar));
+        this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
+    } else {
+        SkScalar    tx, sx = SkScalarDiv(dst.width(), src.width());
+        SkScalar    ty, sy = SkScalarDiv(dst.height(), src.height());
+        bool        xLarger = false;
+
+        if (align != kFill_ScaleToFit) {
+            if (sx > sy) {
+                xLarger = true;
+                sx = sy;
+            } else {
+                sy = sx;
+            }
+        }
+
+        tx = dst.fLeft - SkScalarMul(src.fLeft, sx);
+        ty = dst.fTop - SkScalarMul(src.fTop, sy);
+        if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
+            SkScalar diff;
+
+            if (xLarger) {
+                diff = dst.width() - SkScalarMul(src.width(), sy);
+            } else {
+                diff = dst.height() - SkScalarMul(src.height(), sy);
+            }
+            
+            if (align == kCenter_ScaleToFit) {
+                diff = SkScalarHalf(diff);
+            }
+
+            if (xLarger) {
+                tx += diff;
+            } else {
+                ty += diff;
+            }
+        }
+
+        fMat[kMScaleX] = sx;
+        fMat[kMScaleY] = sy;
+        fMat[kMTransX] = tx;
+        fMat[kMTransY] = ty;
+        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMPersp0] = fMat[kMPersp1] = 0;
+
+        this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
+    }
+    // shared cleanup
+    fMat[kMPersp2] = kMatrix22Elem;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+    static inline int fixmuladdmul(float a, float b, float c, float d,
+                                   float* result) {
+        *result = SkDoubleToFloat((double)a * b + (double)c * d);
+        return true;
+    }
+
+    static inline bool rowcol3(const float row[], const float col[],
+                               float* result) {
+        *result = row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
+        return true;
+    }
+
+    static inline int negifaddoverflows(float& result, float a, float b) {
+        result = a + b;
+        return 0;
+    }
+#else
+    static inline bool fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d,
+                                    SkFixed* result) {
+        Sk64    tmp1, tmp2;
+        tmp1.setMul(a, b);
+        tmp2.setMul(c, d);
+        tmp1.add(tmp2);
+        if (tmp1.isFixed()) {
+            *result = tmp1.getFixed();
+            return true;
+        }
+        return false;
+    }
+
+    static inline SkFixed fracmuladdmul(SkFixed a, SkFract b, SkFixed c,
+                                        SkFract d) {
+        Sk64 tmp1, tmp2;
+        tmp1.setMul(a, b);
+        tmp2.setMul(c, d);
+        tmp1.add(tmp2);
+        return tmp1.getFract();
+    }
+
+    static inline bool rowcol3(const SkFixed row[], const SkFixed col[],
+                               SkFixed* result) {
+        Sk64 tmp1, tmp2;
+
+        tmp1.setMul(row[0], col[0]);    // N * fixed
+        tmp2.setMul(row[1], col[3]);    // N * fixed
+        tmp1.add(tmp2);
+
+        tmp2.setMul(row[2], col[6]);    // N * fract
+        tmp2.roundRight(14);            // make it fixed
+        tmp1.add(tmp2);
+
+        if (tmp1.isFixed()) {
+            *result = tmp1.getFixed();
+            return true;
+        }
+        return false;
+    }
+
+    static inline int negifaddoverflows(SkFixed& result, SkFixed a, SkFixed b) {
+        SkFixed c = a + b;
+        result = c;
+        return (c ^ a) & (c ^ b);
+    }
+#endif
+
+static void normalize_perspective(SkScalar mat[9]) {
+    if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > kMatrix22Elem) {
+        for (int i = 0; i < 9; i++)
+            mat[i] = SkScalarHalf(mat[i]);
+    }
+}
+
+bool SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
+    TypeMask aType = a.getPerspectiveTypeMaskOnly();
+    TypeMask bType = b.getPerspectiveTypeMaskOnly();
+
+    if (a.isTriviallyIdentity()) {
+        *this = b;
+    } else if (b.isTriviallyIdentity()) {
+        *this = a;
+    } else {
+        SkMatrix tmp;
+
+        if ((aType | bType) & kPerspective_Mask) {
+            if (!rowcol3(&a.fMat[0], &b.fMat[0], &tmp.fMat[kMScaleX])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[0], &b.fMat[1], &tmp.fMat[kMSkewX])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[0], &b.fMat[2], &tmp.fMat[kMTransX])) {
+                return false;
+            }
+
+            if (!rowcol3(&a.fMat[3], &b.fMat[0], &tmp.fMat[kMSkewY])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[3], &b.fMat[1], &tmp.fMat[kMScaleY])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[3], &b.fMat[2], &tmp.fMat[kMTransY])) {
+                return false;
+            }
+
+            if (!rowcol3(&a.fMat[6], &b.fMat[0], &tmp.fMat[kMPersp0])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[6], &b.fMat[1], &tmp.fMat[kMPersp1])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[6], &b.fMat[2], &tmp.fMat[kMPersp2])) {
+                return false;
+            }
+
+            normalize_perspective(tmp.fMat);
+            tmp.setTypeMask(kUnknown_Mask);
+        } else {    // not perspective
+            if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMScaleX],
+                    a.fMat[kMSkewX], b.fMat[kMSkewY], &tmp.fMat[kMScaleX])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMSkewX],
+                      a.fMat[kMSkewX], b.fMat[kMScaleY], &tmp.fMat[kMSkewX])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMTransX],
+                      a.fMat[kMSkewX], b.fMat[kMTransY], &tmp.fMat[kMTransX])) {
+                return false;
+            }
+            if (negifaddoverflows(tmp.fMat[kMTransX], tmp.fMat[kMTransX],
+                                  a.fMat[kMTransX]) < 0) {
+                return false;
+            }
+
+            if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMScaleX],
+                      a.fMat[kMScaleY], b.fMat[kMSkewY], &tmp.fMat[kMSkewY])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMSkewX],
+                    a.fMat[kMScaleY], b.fMat[kMScaleY], &tmp.fMat[kMScaleY])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMTransX],
+                     a.fMat[kMScaleY], b.fMat[kMTransY], &tmp.fMat[kMTransY])) {
+                return false;
+            }
+            if (negifaddoverflows(tmp.fMat[kMTransY], tmp.fMat[kMTransY],
+                                  a.fMat[kMTransY]) < 0) {
+                return false;
+            }
+
+            tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0;
+            tmp.fMat[kMPersp2] = kMatrix22Elem;
+            //SkDebugf("Concat mat non-persp type: %d\n", tmp.getType());
+            //SkASSERT(!(tmp.getType() & kPerspective_Mask));
+            tmp.setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+        }
+        *this = tmp;
+    }
+    return true;
+}
+
+bool SkMatrix::preConcat(const SkMatrix& mat) {
+    // check for identity first, so we don't do a needless copy of ourselves
+    // to ourselves inside setConcat()
+    return mat.isIdentity() || this->setConcat(*this, mat);
+}
+
+bool SkMatrix::postConcat(const SkMatrix& mat) {
+    // check for identity first, so we don't do a needless copy of ourselves
+    // to ourselves inside setConcat()
+    return mat.isIdentity() || this->setConcat(mat, *this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  Matrix inversion is very expensive, but also the place where keeping
+    precision may be most important (here and matrix concat). Hence to avoid
+    bitmap blitting artifacts when walking the inverse, we use doubles for
+    the intermediate math, even though we know that is more expensive.
+    The fixed counter part is us using Sk64 for temp calculations.
+ */
+
+#ifdef SK_SCALAR_IS_FLOAT
+    typedef double SkDetScalar;
+    #define SkPerspMul(a, b)            SkScalarMul(a, b)
+    #define SkScalarMulShift(a, b, s)   SkDoubleToFloat((a) * (b))
+    static double sk_inv_determinant(const float mat[9], int isPerspective,
+                                    int* /* (only used in Fixed case) */) {
+        double det;
+
+        if (isPerspective) {
+            det =   mat[SkMatrix::kMScaleX] * ((double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp2] - (double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp1]) +
+                    mat[SkMatrix::kMSkewX] * ((double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp0] - (double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp2]) +
+                    mat[SkMatrix::kMTransX] * ((double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp1] - (double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp0]);
+        } else {
+            det =   (double)mat[SkMatrix::kMScaleX] * mat[SkMatrix::kMScaleY] - (double)mat[SkMatrix::kMSkewX] * mat[SkMatrix::kMSkewY];
+        }
+
+        // Since the determinant is on the order of the cube of the matrix members,
+        // compare to the cube of the default nearly-zero constant (although an
+        // estimate of the condition number would be better if it wasn't so expensive).
+        if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
+            return 0;
+        }
+        return 1.0 / det;
+    }
+    // we declar a,b,c,d to all be doubles, because we want to perform
+    // double-precision muls and subtract, even though the original values are
+    // from the matrix, which are floats.
+    static float inline mul_diff_scale(double a, double b, double c, double d,
+                                       double scale) {
+        return SkDoubleToFloat((a * b - c * d) * scale);
+    }
+#else
+    typedef SkFixed SkDetScalar;
+    #define SkPerspMul(a, b)            SkFractMul(a, b)
+    #define SkScalarMulShift(a, b, s)   SkMulShift(a, b, s)
+    static void set_muladdmul(Sk64* dst, int32_t a, int32_t b, int32_t c,
+                              int32_t d) {
+        Sk64 tmp;
+        dst->setMul(a, b);
+        tmp.setMul(c, d);
+        dst->add(tmp);
+    }
+
+    static SkFixed sk_inv_determinant(const SkFixed mat[9], int isPerspective,
+                                      int* shift) {
+        Sk64    tmp1, tmp2;
+
+        if (isPerspective) {
+            tmp1.setMul(mat[SkMatrix::kMScaleX], fracmuladdmul(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2], -mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1]));
+            tmp2.setMul(mat[SkMatrix::kMSkewX], fracmuladdmul(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0], -mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2]));
+            tmp1.add(tmp2);
+            tmp2.setMul(mat[SkMatrix::kMTransX], fracmuladdmul(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1], -mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]));
+            tmp1.add(tmp2);
+        } else {
+            tmp1.setMul(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY]);
+            tmp2.setMul(mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
+            tmp1.sub(tmp2);
+        }
+
+        int s = tmp1.getClzAbs();
+        *shift = s;
+
+        SkFixed denom;
+        if (s <= 32) {
+            denom = tmp1.getShiftRight(33 - s);
+        } else {
+            denom = (int32_t)tmp1.fLo << (s - 33);
+        }
+
+        if (denom == 0) {
+            return 0;
+        }
+        /** This could perhaps be a special fractdiv function, since both of its
+            arguments are known to have bit 31 clear and bit 30 set (when they
+            are made positive), thus eliminating the need for calling clz()
+        */
+        return SkFractDiv(SK_Fract1, denom);
+    }
+#endif
+
+void SkMatrix::SetAffineIdentity(SkScalar affine[6]) {
+    affine[kAScaleX] = SK_Scalar1;
+    affine[kASkewY] = 0;
+    affine[kASkewX] = 0;
+    affine[kAScaleY] = SK_Scalar1;
+    affine[kATransX] = 0;
+    affine[kATransY] = 0;
+}
+
+bool SkMatrix::asAffine(SkScalar affine[6]) const {
+    if (this->hasPerspective()) {
+        return false;
+    }
+    if (affine) {
+        affine[kAScaleX] = this->fMat[kMScaleX];
+        affine[kASkewY] = this->fMat[kMSkewY];
+        affine[kASkewX] = this->fMat[kMSkewX];
+        affine[kAScaleY] = this->fMat[kMScaleY];
+        affine[kATransX] = this->fMat[kMTransX];
+        affine[kATransY] = this->fMat[kMTransY];
+    }
+    return true;
+}
+
+bool SkMatrix::invert(SkMatrix* inv) const {
+    int         isPersp = this->hasPerspective();
+    int         shift;
+    SkDetScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
+
+    if (scale == 0) { // underflow
+        return false;
+    }
+
+    if (inv) {
+        SkMatrix tmp;
+        if (inv == this) {
+            inv = &tmp;
+        }
+        inv->setTypeMask(kUnknown_Mask);
+
+        if (isPersp) {
+            shift = 61 - shift;
+            inv->fMat[kMScaleX] = SkScalarMulShift(SkPerspMul(fMat[kMScaleY], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransY], fMat[kMPersp1]), scale, shift);
+            inv->fMat[kMSkewX]  = SkScalarMulShift(SkPerspMul(fMat[kMTransX], fMat[kMPersp1]) - SkPerspMul(fMat[kMSkewX],  fMat[kMPersp2]), scale, shift);
+            inv->fMat[kMTransX] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMTransY]) - SkScalarMul(fMat[kMTransX], fMat[kMScaleY]), scale, shift);
+
+            inv->fMat[kMSkewY]  = SkScalarMulShift(SkPerspMul(fMat[kMTransY], fMat[kMPersp0]) - SkPerspMul(fMat[kMSkewY],   fMat[kMPersp2]), scale, shift);
+            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[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
+            if (SkAbs32(inv->fMat[kMPersp2]) > SK_Fixed1) {
+                Sk64    tmp;
+
+                tmp.set(SK_Fract1);
+                tmp.shiftLeft(16);
+                tmp.div(inv->fMat[kMPersp2], Sk64::kRound_DivOption);
+
+                SkFract scale = tmp.get32();
+
+                for (int i = 0; i < 9; i++) {
+                    inv->fMat[i] = SkFractMul(inv->fMat[i], scale);
+                }
+            }
+            inv->fMat[kMPersp2] = SkFixedToFract(inv->fMat[kMPersp2]);
+#endif
+            inv->setTypeMask(kUnknown_Mask);
+        } else {   // not perspective
+#ifdef SK_SCALAR_IS_FIXED
+            Sk64    tx, ty;
+            int     clzNumer;
+
+            // check the 2x2 for overflow
+            {
+                int32_t value = SkAbs32(fMat[kMScaleY]);
+                value |= SkAbs32(fMat[kMSkewX]);
+                value |= SkAbs32(fMat[kMScaleX]);
+                value |= SkAbs32(fMat[kMSkewY]);
+                clzNumer = SkCLZ(value);
+                if (shift - clzNumer > 31)
+                    return false;   // overflow
+            }
+
+            set_muladdmul(&tx, fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX]);
+            set_muladdmul(&ty, fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY]);
+            // check tx,ty for overflow
+            clzNumer = SkCLZ(SkAbs32(tx.fHi) | SkAbs32(ty.fHi));
+            if (shift - clzNumer > 14) {
+                return false;   // overflow
+            }
+
+            int fixedShift = 61 - shift;
+            int sk64shift = 44 - shift + clzNumer;
+
+            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);
+#else
+            inv->fMat[kMScaleX] = SkDoubleToFloat(fMat[kMScaleY] * scale);
+            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],
+                                        fMat[kMScaleX], fMat[kMTransY], scale);
+#endif
+            inv->fMat[kMPersp0] = 0;
+            inv->fMat[kMPersp1] = 0;
+            inv->fMat[kMPersp2] = kMatrix22Elem;
+            inv->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+        }
+
+        if (inv == &tmp) {
+            *(SkMatrix*)this = tmp;
+        }
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
+                            const SkPoint src[], int count) {
+    SkASSERT(m.getType() == 0);
+
+    if (dst != src && count > 0)
+        memcpy(dst, src, count * sizeof(SkPoint));
+}
+
+void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
+                         const SkPoint src[], int count) {
+    SkASSERT(m.getType() == kTranslate_Mask);
+
+    if (count > 0) {
+        SkScalar tx = m.fMat[kMTransX];
+        SkScalar ty = m.fMat[kMTransY];
+        do {
+            dst->fY = src->fY + ty;
+            dst->fX = src->fX + tx;
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
+                         const SkPoint src[], int count) {
+    SkASSERT(m.getType() == kScale_Mask);
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        do {
+            dst->fY = SkScalarMul(src->fY, my);
+            dst->fX = SkScalarMul(src->fX, mx);
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
+                              const SkPoint src[], int count) {
+    SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        SkScalar tx = m.fMat[kMTransX];
+        SkScalar ty = m.fMat[kMTransY];
+        do {
+            dst->fY = SkScalarMulAdd(src->fY, my, ty);
+            dst->fX = SkScalarMulAdd(src->fX, mx, tx);
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
+                       const SkPoint src[], int count) {
+    SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        SkScalar kx = m.fMat[kMSkewX];
+        SkScalar ky = m.fMat[kMSkewY];
+        do {
+            SkScalar sy = src->fY;
+            SkScalar sx = src->fX;
+            src += 1;
+            dst->fY = SkScalarMul(sx, ky) + SkScalarMul(sy, my);
+            dst->fX = SkScalarMul(sx, mx) + SkScalarMul(sy, kx);
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
+                            const SkPoint src[], int count) {
+    SkASSERT(!m.hasPerspective());
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        SkScalar kx = m.fMat[kMSkewX];
+        SkScalar ky = m.fMat[kMSkewY];
+        SkScalar tx = m.fMat[kMTransX];
+        SkScalar ty = m.fMat[kMTransY];
+        do {
+            SkScalar sy = src->fY;
+            SkScalar sx = src->fX;
+            src += 1;
+            dst->fY = SkScalarMul(sx, ky) + SkScalarMulAdd(sy, my, ty);
+            dst->fX = SkScalarMul(sx, mx) + SkScalarMulAdd(sy, kx, tx);
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
+                         const SkPoint src[], int count) {
+    SkASSERT(m.hasPerspective());
+
+#ifdef SK_SCALAR_IS_FIXED
+    SkFixed persp2 = SkFractToFixed(m.fMat[kMPersp2]);
+#endif
+
+    if (count > 0) {
+        do {
+            SkScalar sy = src->fY;
+            SkScalar sx = src->fX;
+            src += 1;
+
+            SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
+                         SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
+            SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
+                         SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
+#ifdef SK_SCALAR_IS_FIXED
+            SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
+                        SkFractMul(sy, m.fMat[kMPersp1]) + persp2;
+#else
+            float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
+                      SkScalarMulAdd(sy, m.fMat[kMPersp1], m.fMat[kMPersp2]);
+#endif
+            if (z) {
+                z = SkScalarFastInvert(z);
+            }
+
+            dst->fY = SkScalarMul(y, z);
+            dst->fX = SkScalarMul(x, z);
+            dst += 1;
+        } while (--count);
+    }
+}
+
+const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
+    SkMatrix::Identity_pts, SkMatrix::Trans_pts,
+    SkMatrix::Scale_pts,    SkMatrix::ScaleTrans_pts,
+    SkMatrix::Rot_pts,      SkMatrix::RotTrans_pts,
+    SkMatrix::Rot_pts,      SkMatrix::RotTrans_pts,
+    // repeat the persp proc 8 times
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts
+};
+
+void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
+    SkASSERT((dst && src && count > 0) || count == 0);
+    // no partial overlap
+    SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= count);
+
+    this->getMapPtsProc()(*this, dst, src, count);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
+    if (this->hasPerspective()) {
+        SkPoint origin;
+
+        MapXYProc proc = this->getMapXYProc();
+        proc(*this, 0, 0, &origin);
+
+        for (int i = count - 1; i >= 0; --i) {
+            SkPoint tmp;
+
+            proc(*this, src[i].fX, src[i].fY, &tmp);
+            dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
+        }
+    } else {
+        SkMatrix tmp = *this;
+
+        tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
+        tmp.clearTypeMask(kTranslate_Mask);
+        tmp.mapPoints(dst, src, count);
+    }
+}
+
+bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
+    SkASSERT(dst && &src);
+
+    if (this->rectStaysRect()) {
+        this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
+        dst->sort();
+        return true;
+    } else {
+        SkPoint quad[4];
+
+        src.toQuad(quad);
+        this->mapPoints(quad, quad, 4);
+        dst->set(quad, 4);
+        return false;
+    }
+}
+
+SkScalar SkMatrix::mapRadius(SkScalar radius) const {
+    SkVector    vec[2];
+
+    vec[0].set(radius, 0);
+    vec[1].set(0, radius);
+    this->mapVectors(vec, 2);
+
+    SkScalar d0 = vec[0].length();
+    SkScalar d1 = vec[1].length();
+
+    return SkScalarMean(d0, d1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                        SkPoint* pt) {
+    SkASSERT(m.hasPerspective());
+
+    SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
+                 SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
+    SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
+                 SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
+#ifdef SK_SCALAR_IS_FIXED
+    SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
+                SkFractMul(sy, m.fMat[kMPersp1]) +
+                SkFractToFixed(m.fMat[kMPersp2]);
+#else
+    float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
+              SkScalarMul(sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
+#endif
+    if (z) {
+        z = SkScalarFastInvert(z);
+    }
+    pt->fX = SkScalarMul(x, z);
+    pt->fY = SkScalarMul(y, z);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+static SkFixed fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d) {
+    Sk64    tmp, tmp1;
+
+    tmp.setMul(a, b);
+    tmp1.setMul(c, d);
+    return tmp.addGetFixed(tmp1);
+//  tmp.add(tmp1);
+//  return tmp.getFixed();
+}
+#endif
+
+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];
+    pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) +
+             m.fMat[kMTransY];
+#else
+    pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
+             SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
+    pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
+             SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+#endif
+}
+
+void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                      SkPoint* pt) {
+    SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
+    SkASSERT(0 == m.fMat[kMTransX]);
+    SkASSERT(0 == m.fMat[kMTransY]);
+
+#ifdef SK_SCALAR_IS_FIXED
+    pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]);
+    pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]);
+#else
+    pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
+             SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
+    pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
+             SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+#endif
+}
+
+void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                             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]);
+}
+
+void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                        SkPoint* pt) {
+    SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
+             == kScale_Mask);
+    SkASSERT(0 == m.fMat[kMTransX]);
+    SkASSERT(0 == m.fMat[kMTransY]);
+
+    pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]);
+    pt->fY = SkScalarMul(sy, m.fMat[kMScaleY]);
+}
+
+void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                        SkPoint* pt) {
+    SkASSERT(m.getType() == kTranslate_Mask);
+
+    pt->fX = sx + m.fMat[kMTransX];
+    pt->fY = sy + m.fMat[kMTransY];
+}
+
+void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                           SkPoint* pt) {
+    SkASSERT(0 == m.getType());
+
+    pt->fX = sx;
+    pt->fY = sy;
+}
+
+const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
+    SkMatrix::Identity_xy, SkMatrix::Trans_xy,
+    SkMatrix::Scale_xy,    SkMatrix::ScaleTrans_xy,
+    SkMatrix::Rot_xy,      SkMatrix::RotTrans_xy,
+    SkMatrix::Rot_xy,      SkMatrix::RotTrans_xy,
+    // repeat the persp proc 8 times
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy,
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy,
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy,
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
+#ifdef SK_SCALAR_IS_FIXED
+    typedef SkFract             SkPerspElemType;
+    #define PerspNearlyZero(x)  (SkAbs32(x) < (SK_Fract1 >> 26))
+#else
+    typedef float               SkPerspElemType;
+    #define PerspNearlyZero(x)  SkScalarNearlyZero(x, (1.0f / (1 << 26)))
+#endif
+
+bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
+    if (PerspNearlyZero(fMat[kMPersp0])) {
+        if (stepX || stepY) {
+            if (PerspNearlyZero(fMat[kMPersp1]) &&
+                    PerspNearlyZero(fMat[kMPersp2] - kMatrix22Elem)) {
+                if (stepX) {
+                    *stepX = SkScalarToFixed(fMat[kMScaleX]);
+                }
+                if (stepY) {
+                    *stepY = SkScalarToFixed(fMat[kMSkewY]);
+                }
+            } else {
+#ifdef SK_SCALAR_IS_FIXED
+                SkFixed z = SkFractMul(y, fMat[kMPersp1]) +
+                            SkFractToFixed(fMat[kMPersp2]);
+#else
+                float z = y * fMat[kMPersp1] + fMat[kMPersp2];
+#endif
+                if (stepX) {
+                    *stepX = SkScalarToFixed(SkScalarDiv(fMat[kMScaleX], z));
+                }
+                if (stepY) {
+                    *stepY = SkScalarToFixed(SkScalarDiv(fMat[kMSkewY], z));
+                }
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPerspIter.h"
+
+SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
+        : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
+    SkPoint pt;
+
+    SkMatrix::Persp_xy(m, x0, y0, &pt);
+    fX = SkScalarToFixed(pt.fX);
+    fY = SkScalarToFixed(pt.fY);
+}
+
+int SkPerspIter::next() {
+    int n = fCount;
+    
+    if (0 == n) {
+        return 0;
+    }
+    SkPoint pt;
+    SkFixed x = fX;
+    SkFixed y = fY;
+    SkFixed dx, dy;
+
+    if (n >= kCount) {
+        n = kCount;
+        fSX += SkIntToScalar(kCount);
+        SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
+        fX = SkScalarToFixed(pt.fX);
+        fY = SkScalarToFixed(pt.fY);
+        dx = (fX - x) >> kShift;
+        dy = (fY - y) >> kShift;
+    } else {
+        fSX += SkIntToScalar(n);
+        SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
+        fX = SkScalarToFixed(pt.fX);
+        fY = SkScalarToFixed(pt.fY);
+        dx = (fX - x) / n;
+        dy = (fY - y) / n;
+    }
+
+    SkFixed* p = fStorage;
+    for (int i = 0; i < n; i++) {
+        *p++ = x; x += dx;
+        *p++ = y; y += dy;
+    }
+    
+    fCount -= n;
+    return n;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+
+static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
+    SkFixed x = SK_Fixed1, y = SK_Fixed1;
+    SkPoint pt1, pt2;
+    Sk64    w1, w2;
+
+    if (count > 1) {
+        pt1.fX = poly[1].fX - poly[0].fX;
+        pt1.fY = poly[1].fY - poly[0].fY;
+        y = SkPoint::Length(pt1.fX, pt1.fY);
+        if (y == 0) {
+            return false;
+        }
+        switch (count) {
+            case 2:
+                break;
+            case 3:
+                pt2.fX = poly[0].fY - poly[2].fY;
+                pt2.fY = poly[2].fX - poly[0].fX;
+                goto CALC_X;
+            default:
+                pt2.fX = poly[0].fY - poly[3].fY;
+                pt2.fY = poly[3].fX - poly[0].fX;
+            CALC_X:
+                w1.setMul(pt1.fX, pt2.fX);
+                w2.setMul(pt1.fY, pt2.fY);
+                w1.add(w2);
+                w1.div(y, Sk64::kRound_DivOption);
+                if (!w1.is32()) {
+                    return false;
+                }
+                x = w1.get32();
+                break;
+        }
+    }
+    pt->set(x, y);
+    return true;
+}
+
+bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scalePt) {
+    // need to check if SkFixedDiv overflows...
+
+    const SkFixed scale = scalePt.fY;
+    dst->fMat[kMScaleX] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
+    dst->fMat[kMSkewY]  = SkFixedDiv(srcPt[0].fX - srcPt[1].fX, scale);
+    dst->fMat[kMPersp0] = 0;
+    dst->fMat[kMSkewX]  = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale);
+    dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
+    dst->fMat[kMPersp1] = 0;
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = SK_Fract1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    // really, need to check if SkFixedDiv overflow'd
+
+    dst->fMat[kMScaleX] = SkFixedDiv(srcPt[2].fX - srcPt[0].fX, scale.fX);
+    dst->fMat[kMSkewY]  = SkFixedDiv(srcPt[2].fY - srcPt[0].fY, scale.fX);
+    dst->fMat[kMPersp0] = 0;
+    dst->fMat[kMSkewX]  = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale.fY);
+    dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale.fY);
+    dst->fMat[kMPersp1] = 0;
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = SK_Fract1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    SkFract a1, a2;
+    SkFixed x0, y0, x1, y1, x2, y2;
+
+    x0 = srcPt[2].fX - srcPt[0].fX;
+    y0 = srcPt[2].fY - srcPt[0].fY;
+    x1 = srcPt[2].fX - srcPt[1].fX;
+    y1 = srcPt[2].fY - srcPt[1].fY;
+    x2 = srcPt[2].fX - srcPt[3].fX;
+    y2 = srcPt[2].fY - srcPt[3].fY;
+
+    /* check if abs(x2) > abs(y2) */
+    if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
+        SkFixed denom = SkMulDiv(x1, y2, x2) - y1;
+        if (0 == denom) {
+            return false;
+        }
+        a1 = SkFractDiv(SkMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
+    } else {
+        SkFixed denom = x1 - SkMulDiv(y1, x2, y2);
+        if (0 == denom) {
+            return false;
+        }
+        a1 = SkFractDiv(x0 - x1 - SkMulDiv(y0 - y1, x2, y2), denom);
+    }
+
+    /* check if abs(x1) > abs(y1) */
+    if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
+        SkFixed denom = y2 - SkMulDiv(x2, y1, x1);
+        if (0 == denom) {
+            return false;
+        }
+        a2 = SkFractDiv(y0 - y2 - SkMulDiv(x0 - x2, y1, x1), denom);
+    } else {
+        SkFixed denom = SkMulDiv(y2, x1, y1) - x2;
+        if (0 == denom) {
+            return false;
+        }
+        a2 = SkFractDiv(SkMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
+    }
+
+    // need to check if SkFixedDiv overflows...
+    dst->fMat[kMScaleX] = SkFixedDiv(SkFractMul(a2, srcPt[3].fX) +
+                                     srcPt[3].fX - srcPt[0].fX, scale.fX);
+    dst->fMat[kMSkewY]  = SkFixedDiv(SkFractMul(a2, srcPt[3].fY) +
+                                     srcPt[3].fY - srcPt[0].fY, scale.fX);
+    dst->fMat[kMPersp0] = SkFixedDiv(a2, scale.fX);
+    dst->fMat[kMSkewX]  = SkFixedDiv(SkFractMul(a1, srcPt[1].fX) +
+                                     srcPt[1].fX - srcPt[0].fX, scale.fY);
+    dst->fMat[kMScaleY] = SkFixedDiv(SkFractMul(a1, srcPt[1].fY) +
+                                     srcPt[1].fY - srcPt[0].fY, scale.fY);
+    dst->fMat[kMPersp1] = SkFixedDiv(a1, scale.fY);
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = SK_Fract1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+#else   /* Scalar is float */
+
+static inline bool checkForZero(float x) {
+    return x*x == 0;
+}
+
+static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
+    float   x = 1, y = 1;
+    SkPoint pt1, pt2;
+
+    if (count > 1) {
+        pt1.fX = poly[1].fX - poly[0].fX;
+        pt1.fY = poly[1].fY - poly[0].fY;
+        y = SkPoint::Length(pt1.fX, pt1.fY);
+        if (checkForZero(y)) {
+            return false;
+        }
+        switch (count) {
+            case 2:
+                break;
+            case 3:
+                pt2.fX = poly[0].fY - poly[2].fY;
+                pt2.fY = poly[2].fX - poly[0].fX;
+                goto CALC_X;
+            default:
+                pt2.fX = poly[0].fY - poly[3].fY;
+                pt2.fY = poly[3].fX - poly[0].fX;
+            CALC_X:
+                x = SkScalarDiv(SkScalarMul(pt1.fX, pt2.fX) +
+                                SkScalarMul(pt1.fY, pt2.fY), y);
+                break;
+        }
+    }
+    pt->set(x, y);
+    return true;
+}
+
+bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    float invScale = 1 / scale.fY;
+
+    dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
+    dst->fMat[kMPersp0] = 0;
+    dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
+    dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMPersp1] = 0;
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = 1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    float invScale = 1 / scale.fX;
+    dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
+    dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMPersp0] = 0;
+
+    invScale = 1 / scale.fY;
+    dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
+    dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMPersp1] = 0;
+
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = 1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    float   a1, a2;
+    float   x0, y0, x1, y1, x2, y2;
+
+    x0 = srcPt[2].fX - srcPt[0].fX;
+    y0 = srcPt[2].fY - srcPt[0].fY;
+    x1 = srcPt[2].fX - srcPt[1].fX;
+    y1 = srcPt[2].fY - srcPt[1].fY;
+    x2 = srcPt[2].fX - srcPt[3].fX;
+    y2 = srcPt[2].fY - srcPt[3].fY;
+
+    /* check if abs(x2) > abs(y2) */
+    if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
+        float denom = SkScalarMulDiv(x1, y2, x2) - y1;
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a1 = SkScalarDiv(SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
+    } else {
+        float denom = x1 - SkScalarMulDiv(y1, x2, y2);
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a1 = SkScalarDiv(x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2), denom);
+    }
+
+    /* check if abs(x1) > abs(y1) */
+    if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
+        float denom = y2 - SkScalarMulDiv(x2, y1, x1);
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a2 = SkScalarDiv(y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1), denom);
+    } else {
+        float denom = SkScalarMulDiv(y2, x1, y1) - x2;
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a2 = SkScalarDiv(SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
+    }
+
+    float invScale = 1 / scale.fX;
+    dst->fMat[kMScaleX] = SkScalarMul(SkScalarMul(a2, srcPt[3].fX) +
+                                      srcPt[3].fX - srcPt[0].fX, invScale);
+    dst->fMat[kMSkewY] = SkScalarMul(SkScalarMul(a2, srcPt[3].fY) +
+                                     srcPt[3].fY - srcPt[0].fY, invScale);
+    dst->fMat[kMPersp0] = SkScalarMul(a2, invScale);
+    invScale = 1 / scale.fY;
+    dst->fMat[kMSkewX] = SkScalarMul(SkScalarMul(a1, srcPt[1].fX) +
+                                     srcPt[1].fX - srcPt[0].fX, invScale);
+    dst->fMat[kMScaleY] = SkScalarMul(SkScalarMul(a1, srcPt[1].fY) +
+                                      srcPt[1].fY - srcPt[0].fY, invScale);
+    dst->fMat[kMPersp1] = SkScalarMul(a1, invScale);
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = 1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+#endif
+
+typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
+
+/*  Taken from Rob Johnson's original sample code in QuickDraw GX
+*/
+bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
+                             int count) {
+    if ((unsigned)count > 4) {
+        SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
+        return false;
+    }
+
+    if (0 == count) {
+        this->reset();
+        return true;
+    }
+    if (1 == count) {
+        this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
+        return true;
+    }
+
+    SkPoint scale;
+    if (!poly_to_point(&scale, src, count) ||
+            SkScalarNearlyZero(scale.fX) ||
+            SkScalarNearlyZero(scale.fY)) {
+        return false;
+    }
+
+    static const PolyMapProc gPolyMapProcs[] = {
+        SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
+    };
+    PolyMapProc proc = gPolyMapProcs[count - 2];
+
+    SkMatrix tempMap, result;
+    tempMap.setTypeMask(kUnknown_Mask);
+
+    if (!proc(src, &tempMap, scale)) {
+        return false;
+    }
+    if (!tempMap.invert(&result)) {
+        return false;
+    }
+    if (!proc(dst, &tempMap, scale)) {
+        return false;
+    }
+    if (!result.setConcat(tempMap, result)) {
+        return false;
+    }
+    *this = result;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkScalar SkMatrix::getMaxStretch() const {
+    TypeMask mask = this->getType();
+
+    if (this->hasPerspective()) {
+        return -SK_Scalar1;
+    }
+    if (this->isIdentity()) {
+        return SK_Scalar1;
+    }
+    if (!(mask & kAffine_Mask)) {
+        return SkMaxScalar(SkScalarAbs(fMat[kMScaleX]),
+                           SkScalarAbs(fMat[kMScaleY]));
+    }
+    // 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
+    SkScalar a = SkScalarMul(fMat[kMScaleX], fMat[kMScaleX]) +
+                 SkScalarMul(fMat[kMSkewY],  fMat[kMSkewY]);
+    SkScalar b = SkScalarMul(fMat[kMScaleX], fMat[kMSkewX]) +
+                 SkScalarMul(fMat[kMScaleY], fMat[kMSkewY]);
+    SkScalar c = SkScalarMul(fMat[kMSkewX],  fMat[kMSkewX]) +
+                 SkScalarMul(fMat[kMScaleY], fMat[kMScaleY]);
+    // 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).
+    SkScalar largerRoot;
+    SkScalar bSqd = SkScalarMul(b,b);
+    // if upper left 2x2 is orthogonal save some math
+    if (bSqd <= SK_ScalarNearlyZero) {
+        largerRoot = SkMaxScalar(a, c);
+    } else {
+        SkScalar aminusc = a - c;
+        SkScalar apluscdiv2 = SkScalarHalf(a + c);
+        SkScalar x = SkScalarHalf(SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd));
+        largerRoot = apluscdiv2 + x;
+    }
+    return SkScalarSqrt(largerRoot);
+}
+
+const SkMatrix& SkMatrix::I() {
+    static SkMatrix gIdentity;
+    static bool gOnce;
+    if (!gOnce) {
+        gIdentity.reset();
+        gOnce = true;
+    }
+    return gIdentity;
+};
+
+const SkMatrix& SkMatrix::InvalidMatrix() {
+    static SkMatrix gInvalid;
+    static bool gOnce;
+    if (!gOnce) {
+        gInvalid.setAll(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
+                        SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
+                        SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+        gInvalid.getType(); // force the type to be computed
+        gOnce = true;
+    }
+    return gInvalid;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t SkMatrix::flatten(void* buffer) const {
+    // TODO write less for simple matrices
+    if (buffer) {
+        memcpy(buffer, fMat, 9 * sizeof(SkScalar));
+    }
+    return 9 * sizeof(SkScalar);
+}
+
+uint32_t SkMatrix::unflatten(const void* buffer) {
+    if (buffer) {
+        memcpy(fMat, buffer, 9 * sizeof(SkScalar));
+        this->setTypeMask(kUnknown_Mask);
+    }
+    return 9 * sizeof(SkScalar);
+}
+
+void SkMatrix::dump() const {
+    SkString str;
+    this->toDumpString(&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]",
+#ifdef SK_SCALAR_IS_FLOAT
+             fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
+             fMat[6], fMat[7], fMat[8]);
+#else
+    SkFixedToFloat(fMat[0]), SkFixedToFloat(fMat[1]), SkFixedToFloat(fMat[2]),
+    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
+}
diff --git a/legacy/src/core/SkMemory_stdlib.cpp b/legacy/src/core/SkMemory_stdlib.cpp
new file mode 100644
index 0000000..033b331
--- /dev/null
+++ b/legacy/src/core/SkMemory_stdlib.cpp
@@ -0,0 +1,268 @@
+
+/*
+ * 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 "SkTypes.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef SK_DEBUG
+    #define SK_TAG_BLOCKS
+    // #define SK_TRACK_ALLOC  // enable to see a printf for every alloc/free
+    // #define SK_CHECK_TAGS   // enable to double-check debugging link list
+#endif
+
+#ifdef SK_TAG_BLOCKS
+
+#include "SkThread.h"
+
+// size this (as a multiple of 4) so that the total offset to the internal data
+// is at least a multiple of 8 (since some clients of our malloc may require
+// that.
+static const char kBlockHeaderTag[] = { 's', 'k', 'i', 'a', '1', '2', '3', '4' };
+static const char kBlockTrailerTag[] = { 'a', 'i', 'k', 's' };
+#define kByteFill 0xCD
+#define kDeleteFill 0xEF
+
+static SkBaseMutex& get_block_mutex() {
+    static SkMutex* gBlockMutex;
+    if (NULL == gBlockMutex) {
+        gBlockMutex = new SkMutex;
+    }
+    return *gBlockMutex;
+}
+
+static struct SkBlockHeader* gHeader;
+
+struct SkBlockHeader {
+    SkBlockHeader* fNext;
+#ifdef SK_CHECK_TAGS
+    SkBlockHeader** fTop; // set to verify in debugger that block was alloc'd / freed with same gHeader
+    SkBlockHeader* fPrevious; // set to see in debugger previous block when corruption happens
+#endif
+    size_t fSize;
+    char fHeader[sizeof(kBlockHeaderTag)];
+    // data goes here. The offset to this point must be a multiple of 8
+    char fTrailer[sizeof(kBlockTrailerTag)];
+
+    void* add(size_t realSize) 
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        InMutexValidate();
+        fNext = gHeader;
+#ifdef SK_CHECK_TAGS
+        fTop = &gHeader;
+        fPrevious = NULL;
+        if (fNext != NULL)
+            fNext->fPrevious = this;
+#endif
+        gHeader = this;
+        fSize = realSize;
+        memcpy(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag));
+        void* result = fTrailer;
+        void* trailer = (char*)result + realSize;
+        memcpy(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag));
+        return result;
+    }
+    
+    static void Dump()
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        InMutexValidate();
+        SkBlockHeader* header = gHeader;
+        int count = 0;
+        size_t size = 0;
+        while (header != NULL) {
+            char scratch[256];
+            char* pos = scratch;
+            size_t size = header->fSize;
+            int* data = (int*)(void*)header->fTrailer;
+            pos += sprintf(pos, "%p 0x%08zx (%7zd)  ",
+                data, size, size);
+            size >>= 2;
+            size_t ints = size > 4 ? 4 : size;
+            size_t index;
+            for (index = 0; index < ints; index++)
+                pos += sprintf(pos, "0x%08x ", data[index]);
+            pos += sprintf(pos, " (");
+            for (index = 0; index < ints; index++)
+                pos += sprintf(pos, "%g ", data[index] / 65536.0f);
+            if (ints > 0)
+                --pos;
+            pos += sprintf(pos, ") \"");
+            size_t chars = size > 16 ? 16 : size;
+            char* chPtr = (char*) data;
+            for (index = 0; index < chars; index++) {
+                char ch = chPtr[index];
+                pos += sprintf(pos, "%c", ch >= ' ' && ch < 0x7f ? ch : '?');
+            }
+            pos += sprintf(pos, "\"");
+            SkDebugf("%s\n", scratch);
+            count++;
+            size += header->fSize;
+            header = header->fNext;
+        }
+        SkDebugf("--- count %d  size 0x%08x (%zd) ---\n", count, size, size);
+    }
+
+    void remove() const
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        SkBlockHeader** findPtr = &gHeader;
+        do {
+            SkBlockHeader* find = *findPtr;
+            SkASSERT(find != NULL);
+            if (find == this) {
+                *findPtr = fNext;
+                break;
+            }
+            findPtr = &find->fNext;
+        } while (true);
+        InMutexValidate();
+        SkASSERT(memcmp(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
+        const char* trailer = fTrailer + fSize;
+        SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
+    }
+    
+    static void Validate()
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        InMutexValidate();
+    }
+
+private:
+    static void InMutexValidate()
+    {
+        SkBlockHeader* header = gHeader;
+        while (header != NULL) {
+            SkASSERT(memcmp(header->fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
+            char* trailer = header->fTrailer + header->fSize;
+            SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
+            header = header->fNext;
+        }
+    }
+};
+
+void ValidateHeap();
+void ValidateHeap()
+{
+    SkBlockHeader::Validate();
+}
+#else
+void ValidateHeap() {}
+#endif
+
+void sk_throw()
+{
+    SkDEBUGFAIL("sk_throw");
+    abort();
+}
+
+void sk_out_of_memory(void)
+{
+    SkDEBUGFAIL("sk_out_of_memory");
+    abort();
+}
+
+void* sk_malloc_throw(size_t size)
+{
+    return sk_malloc_flags(size, SK_MALLOC_THROW);
+}
+
+void* sk_realloc_throw(void* addr, size_t size)
+{
+#ifdef SK_TAG_BLOCKS
+    ValidateHeap();
+    if (addr != NULL) {
+        SkBlockHeader* header = (SkBlockHeader*)
+            ((char*)addr - SK_OFFSETOF(SkBlockHeader, fTrailer));
+        header->remove();
+#ifdef SK_TRACK_ALLOC
+        printf("sk_realloc_throw %p oldSize=%zd\n", addr, header->fSize);
+#endif
+        addr = header;
+    }
+    size_t realSize = size;
+    if (size) 
+        size += sizeof(SkBlockHeader);
+#endif
+
+    void* p = realloc(addr, size);
+    if (size == 0)
+    {
+        ValidateHeap();
+        return p;
+    }
+
+    if (p == NULL)
+        sk_throw();
+#ifdef SK_TAG_BLOCKS
+    else
+    {
+        SkBlockHeader* header = (SkBlockHeader*) p;
+        p = header->add(realSize);
+#ifdef SK_TRACK_ALLOC
+        printf("sk_realloc_throw %p size=%zd\n", p, realSize);
+#endif
+    }
+#endif
+    ValidateHeap();
+    return p;
+}
+
+void sk_free(void* p)
+{
+    if (p)
+    {
+        ValidateHeap();
+#ifdef SK_TAG_BLOCKS
+        SkBlockHeader* header = (SkBlockHeader*) 
+            ((char*)p - SK_OFFSETOF(SkBlockHeader, fTrailer));
+        header->remove();
+#ifdef SK_TRACK_ALLOC
+        printf("sk_free %p size=%zd\n", p, header->fSize);
+#endif
+        size_t size = header->fSize + sizeof(SkBlockHeader);
+        memset(header, kDeleteFill, size);
+        p = header;
+#endif
+        ValidateHeap();
+        free(p);
+        ValidateHeap();
+    }
+}
+
+void* sk_malloc_flags(size_t size, unsigned flags)
+{
+    ValidateHeap();
+#ifdef SK_TAG_BLOCKS
+    size_t realSize = size;
+    size += sizeof(SkBlockHeader);
+#endif
+    
+    void* p = malloc(size);
+    if (p == NULL)
+    {
+        if (flags & SK_MALLOC_THROW)
+            sk_throw();
+    }
+#ifdef SK_TAG_BLOCKS
+    else
+    {
+        SkBlockHeader* header = (SkBlockHeader*) p;
+        p = header->add(realSize);
+        memset(p, kByteFill, realSize);
+#ifdef SK_TRACK_ALLOC
+        printf("sk_malloc_flags %p size=%zd\n", p, realSize);
+#endif
+    }
+#endif
+    ValidateHeap();
+    return p;
+}
+
diff --git a/legacy/src/core/SkMetaData.cpp b/legacy/src/core/SkMetaData.cpp
new file mode 100644
index 0000000..338ec5e
--- /dev/null
+++ b/legacy/src/core/SkMetaData.cpp
@@ -0,0 +1,337 @@
+
+/*
+ * 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 "SkMetaData.h"
+#include "SkRefCnt.h"
+
+struct PtrPair {
+    void*               fPtr;
+    SkMetaData::PtrProc fProc;
+};
+
+void* SkMetaData::RefCntProc(void* ptr, bool doRef) {
+    SkASSERT(ptr);
+    SkRefCnt* refcnt = reinterpret_cast<SkRefCnt*>(ptr);
+
+    if (doRef) {
+        refcnt->ref();
+    } else {
+        refcnt->unref();
+    }
+    return ptr;
+}
+
+SkMetaData::SkMetaData() : fRec(NULL)
+{
+}
+
+SkMetaData::SkMetaData(const SkMetaData& src) : fRec(NULL)
+{
+    *this = src;
+}
+
+SkMetaData::~SkMetaData()
+{
+    this->reset();
+}
+
+void SkMetaData::reset()
+{
+    Rec* rec = fRec;
+    while (rec) {
+        if (kPtr_Type == rec->fType) {
+            PtrPair* pair = (PtrPair*)rec->data();
+            if (pair->fProc && pair->fPtr) {
+                pair->fPtr = pair->fProc(pair->fPtr, false);
+            }
+        }
+        Rec* next = rec->fNext;
+        Rec::Free(rec);
+        rec = next;
+    }
+    fRec = NULL;
+}
+
+SkMetaData& SkMetaData::operator=(const SkMetaData& src)
+{
+    this->reset();
+
+    const Rec* rec = src.fRec;
+    while (rec)
+    {
+        this->set(rec->name(), rec->data(), rec->fDataLen, (Type)rec->fType, rec->fDataCount);
+        rec = rec->fNext;
+    }
+    return *this;
+}
+
+void SkMetaData::setS32(const char name[], int32_t value)
+{
+    (void)this->set(name, &value, sizeof(int32_t), kS32_Type, 1);
+}
+
+void SkMetaData::setScalar(const char name[], SkScalar value)
+{
+    (void)this->set(name, &value, sizeof(SkScalar), kScalar_Type, 1);
+}
+
+SkScalar* SkMetaData::setScalars(const char name[], int count, const SkScalar values[])
+{
+    SkASSERT(count > 0);
+    if (count > 0)
+        return (SkScalar*)this->set(name, values, sizeof(SkScalar), kScalar_Type, count);
+    return NULL;
+}
+
+void SkMetaData::setString(const char name[], const char value[])
+{
+    (void)this->set(name, value, sizeof(char), kString_Type, strlen(value) + 1);
+}
+
+void SkMetaData::setPtr(const char name[], void* ptr, PtrProc proc) {
+    PtrPair pair = { ptr, proc };
+    (void)this->set(name, &pair, sizeof(PtrPair), kPtr_Type, 1);
+}
+
+void SkMetaData::setBool(const char name[], bool value)
+{
+    (void)this->set(name, &value, sizeof(bool), kBool_Type, 1);
+}
+
+void SkMetaData::setData(const char name[], const void* data, size_t byteCount) {
+    (void)this->set(name, data, sizeof(char), kData_Type, byteCount);
+}
+
+void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type type, int count)
+{
+    SkASSERT(name);
+    SkASSERT(dataSize);
+    SkASSERT(count > 0);
+
+    (void)this->remove(name, type);
+
+    size_t  len = strlen(name);
+    Rec*    rec = Rec::Alloc(sizeof(Rec) + dataSize * count + len + 1);
+
+#ifndef SK_DEBUG
+    rec->fType = SkToU8(type);
+#else
+    rec->fType = type;
+#endif
+    rec->fDataLen = SkToU8(dataSize);
+    rec->fDataCount = SkToU16(count);
+    if (data)
+        memcpy(rec->data(), data, dataSize * count);
+    memcpy(rec->name(), name, len + 1);
+
+    if (kPtr_Type == type) {
+        PtrPair* pair = (PtrPair*)rec->data();
+        if (pair->fProc && pair->fPtr) {
+            pair->fPtr = pair->fProc(pair->fPtr, true);
+        }
+    }
+
+    rec->fNext = fRec;
+    fRec = rec;
+    return rec->data();
+}
+
+bool SkMetaData::findS32(const char name[], int32_t* value) const
+{
+    const Rec* rec = this->find(name, kS32_Type);
+    if (rec)
+    {
+        SkASSERT(rec->fDataCount == 1);
+        if (value)
+            *value = *(const int32_t*)rec->data();
+        return true;
+    }
+    return false;
+}
+
+bool SkMetaData::findScalar(const char name[], SkScalar* value) const
+{
+    const Rec* rec = this->find(name, kScalar_Type);
+    if (rec)
+    {
+        SkASSERT(rec->fDataCount == 1);
+        if (value)
+            *value = *(const SkScalar*)rec->data();
+        return true;
+    }
+    return false;
+}
+
+const SkScalar* SkMetaData::findScalars(const char name[], int* count, SkScalar values[]) const
+{
+    const Rec* rec = this->find(name, kScalar_Type);
+    if (rec)
+    {
+        if (count)
+            *count = rec->fDataCount;
+        if (values)
+            memcpy(values, rec->data(), rec->fDataCount * rec->fDataLen);
+        return (const SkScalar*)rec->data();
+    }
+    return NULL;
+}
+
+bool SkMetaData::findPtr(const char name[], void** ptr, PtrProc* proc) const {
+    const Rec* rec = this->find(name, kPtr_Type);
+    if (rec) {
+        SkASSERT(rec->fDataCount == 1);
+        const PtrPair* pair = (const PtrPair*)rec->data();
+        if (ptr) {
+            *ptr = pair->fPtr;
+        }
+        if (proc) {
+            *proc = pair->fProc;
+        }
+        return true;
+    }
+    return false;
+}
+
+const char* SkMetaData::findString(const char name[]) const
+{
+    const Rec* rec = this->find(name, kString_Type);
+    SkASSERT(rec == NULL || rec->fDataLen == sizeof(char));
+    return rec ? (const char*)rec->data() : NULL;
+}
+
+bool SkMetaData::findBool(const char name[], bool* value) const
+{
+    const Rec* rec = this->find(name, kBool_Type);
+    if (rec)
+    {
+        SkASSERT(rec->fDataCount == 1);
+        if (value)
+            *value = *(const bool*)rec->data();
+        return true;
+    }
+    return false;
+}
+
+const void* SkMetaData::findData(const char name[], size_t* length) const {
+    const Rec* rec = this->find(name, kData_Type);
+    if (rec) {
+        SkASSERT(rec->fDataLen == sizeof(char));
+        if (length) {
+            *length = rec->fDataCount;
+        }
+        return rec->data();
+    }
+    return NULL;
+}
+
+const SkMetaData::Rec* SkMetaData::find(const char name[], Type type) const
+{
+    const Rec* rec = fRec;
+    while (rec)
+    {
+        if (rec->fType == type && !strcmp(rec->name(), name))
+            return rec;
+        rec = rec->fNext;
+    }
+    return NULL;
+}
+
+bool SkMetaData::remove(const char name[], Type type) {
+    Rec* rec = fRec;
+    Rec* prev = NULL;
+    while (rec) {
+        Rec* next = rec->fNext;
+        if (rec->fType == type && !strcmp(rec->name(), name)) {
+            if (prev) {
+                prev->fNext = next;
+            } else {
+                fRec = next;
+            }
+
+            if (kPtr_Type == type) {
+                PtrPair* pair = (PtrPair*)rec->data();
+                if (pair->fProc && pair->fPtr) {
+                    (void)pair->fProc(pair->fPtr, false);
+                }
+            }
+            Rec::Free(rec);
+            return true;
+        }
+        prev = rec;
+        rec = next;
+    }
+    return false;
+}
+
+bool SkMetaData::removeS32(const char name[])
+{
+    return this->remove(name, kS32_Type);
+}
+
+bool SkMetaData::removeScalar(const char name[])
+{
+    return this->remove(name, kScalar_Type);
+}
+
+bool SkMetaData::removeString(const char name[])
+{
+    return this->remove(name, kString_Type);
+}
+
+bool SkMetaData::removePtr(const char name[])
+{
+    return this->remove(name, kPtr_Type);
+}
+
+bool SkMetaData::removeBool(const char name[])
+{
+    return this->remove(name, kBool_Type);
+}
+
+bool SkMetaData::removeData(const char name[]) {
+    return this->remove(name, kData_Type);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkMetaData::Iter::Iter(const SkMetaData& metadata) {
+    fRec = metadata.fRec;
+}
+
+void SkMetaData::Iter::reset(const SkMetaData& metadata) {
+    fRec = metadata.fRec;
+}
+
+const char* SkMetaData::Iter::next(SkMetaData::Type* t, int* count) {
+    const char* name = NULL;
+
+    if (fRec) {
+        if (t) {
+            *t = (SkMetaData::Type)fRec->fType;
+        }
+        if (count) {
+            *count = fRec->fDataCount;
+        }
+        name = fRec->name();
+
+        fRec = fRec->fNext;
+    }
+    return name;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkMetaData::Rec* SkMetaData::Rec::Alloc(size_t size) {
+    return (Rec*)sk_malloc_throw(size);
+}
+
+void SkMetaData::Rec::Free(Rec* rec) {
+    sk_free(rec);
+}
+
diff --git a/legacy/src/core/SkPackBits.cpp b/legacy/src/core/SkPackBits.cpp
new file mode 100644
index 0000000..8edd4c0
--- /dev/null
+++ b/legacy/src/core/SkPackBits.cpp
@@ -0,0 +1,411 @@
+
+/*
+ * 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 "SkPackBits.h"
+
+#define GATHER_STATSx
+
+static inline void small_memcpy(void* SK_RESTRICT dst,
+                                const void* SK_RESTRICT src, int n) {
+    SkASSERT(n > 0 && n <= 15);
+    uint8_t* d = (uint8_t*)dst;
+    const uint8_t* s = (const uint8_t*)src;
+    switch (n) {
+        case 15: *d++ = *s++;
+        case 14: *d++ = *s++;
+        case 13: *d++ = *s++;
+        case 12: *d++ = *s++;
+        case 11: *d++ = *s++;
+        case 10: *d++ = *s++;
+        case  9: *d++ = *s++;
+        case  8: *d++ = *s++;
+        case  7: *d++ = *s++;
+        case  6: *d++ = *s++;
+        case  5: *d++ = *s++;
+        case  4: *d++ = *s++;
+        case  3: *d++ = *s++;
+        case  2: *d++ = *s++;
+        case  1: *d++ = *s++;
+        case  0: break;
+    }
+}
+
+static inline void small_memset(void* dst, uint8_t value, int n) {
+    SkASSERT(n > 0 && n <= 15);
+    uint8_t* d = (uint8_t*)dst;
+    switch (n) {
+        case 15: *d++ = value;
+        case 14: *d++ = value;
+        case 13: *d++ = value;
+        case 12: *d++ = value;
+        case 11: *d++ = value;
+        case 10: *d++ = value;
+        case  9: *d++ = value;
+        case  8: *d++ = value;
+        case  7: *d++ = value;
+        case  6: *d++ = value;
+        case  5: *d++ = value;
+        case  4: *d++ = value;
+        case  3: *d++ = value;
+        case  2: *d++ = value;
+        case  1: *d++ = value;
+        case  0: break;
+    }
+}
+
+// can we do better for small counts with our own inlined memcpy/memset?
+
+#define PB_MEMSET(addr, value, count)       \
+do {                                        \
+if ((count) > 15) {                     \
+memset(addr, value, count);         \
+} else {                                \
+small_memset(addr, value, count);   \
+}                                       \
+} while (0)
+
+#define PB_MEMCPY(dst, src, count)      \
+do {                                    \
+    if ((count) > 15) {                 \
+        memcpy(dst, src, count);        \
+    } else {                            \
+        small_memcpy(dst, src, count);  \
+    }                                   \
+} while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef GATHER_STATS
+    static int gMemSetBuckets[129];
+    static int gMemCpyBuckets[129];
+    static int gCounter;
+
+static void register_memset_count(int n) {
+    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++) {
+            if (gMemSetBuckets[i]) {
+                SkDebugf(" %d:%d", i, gMemSetBuckets[i]);
+            }
+        }
+    }
+}
+static void register_memcpy_count(int n) {
+    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++) {
+            if (gMemCpyBuckets[i]) {
+                SkDebugf(" %d:%d", i, gMemCpyBuckets[i]);
+            }
+        }
+    }
+}
+#else
+#define register_memset_count(n)
+#define register_memcpy_count(n)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkPackBits::ComputeMaxSize16(int count) {
+    // worst case is the number of 16bit values (times 2) +
+    // 1 byte per (up to) 128 entries.
+    return ((count + 127) >> 7) + (count << 1);
+}
+
+size_t SkPackBits::ComputeMaxSize8(int count) {
+    // worst case is the number of 8bit values + 1 byte per (up to) 128 entries.
+    return ((count + 127) >> 7) + count;
+}
+
+static uint8_t* flush_same16(uint8_t dst[], uint16_t value, int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n - 1);
+        *dst++ = (uint8_t)(value >> 8);
+        *dst++ = (uint8_t)value;
+        count -= n;
+    }
+    return dst;
+}
+
+static uint8_t* flush_same8(uint8_t dst[], uint8_t value, int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n - 1);
+        *dst++ = (uint8_t)value;
+        count -= n;
+    }
+    return dst;
+}
+
+static uint8_t* flush_diff16(uint8_t* SK_RESTRICT dst,
+                             const uint16_t* SK_RESTRICT src, int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n + 127);
+        PB_MEMCPY(dst, src, n * sizeof(uint16_t));
+        src += n;
+        dst += n * sizeof(uint16_t);
+        count -= n;
+    }
+    return dst;
+}
+
+static uint8_t* flush_diff8(uint8_t* SK_RESTRICT dst,
+                            const uint8_t* SK_RESTRICT src, int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n + 127);
+        PB_MEMCPY(dst, src, n);
+        src += n;
+        dst += n;
+        count -= n;
+    }
+    return dst;
+}
+
+size_t SkPackBits::Pack16(const uint16_t* SK_RESTRICT src, int count,
+                          uint8_t* SK_RESTRICT dst) {
+    uint8_t* origDst = dst;
+    const uint16_t* stop = src + count;
+
+    for (;;) {
+        count = stop - src;
+        SkASSERT(count >= 0);
+        if (count == 0) {
+            return dst - origDst;
+        }
+        if (1 == count) {
+            *dst++ = 0;
+            *dst++ = (uint8_t)(*src >> 8);
+            *dst++ = (uint8_t)*src;
+            return dst - origDst;
+        }
+        
+        unsigned value = *src;
+        const uint16_t* s = src + 1;
+        
+        if (*s == value) { // accumulate same values...
+            do {
+                s++;
+                if (s == stop) {
+                    break;
+                }
+            } while (*s == value);
+            dst = flush_same16(dst, value, s - src);
+        } else {    // accumulate diff values...
+            do {
+                if (++s == stop) {
+                    goto FLUSH_DIFF;
+                }
+            } while (*s != s[-1]);
+            s -= 1; // back up so we don't grab one of the "same" values that follow
+        FLUSH_DIFF:
+            dst = flush_diff16(dst, src, s - src);
+        }
+        src = s;
+    }
+}
+
+size_t SkPackBits::Pack8(const uint8_t* SK_RESTRICT src, int count,
+                         uint8_t* SK_RESTRICT dst) {
+    uint8_t* origDst = dst;
+    const uint8_t* stop = src + count;
+
+    for (;;) {
+        count = stop - src;
+        SkASSERT(count >= 0);
+        if (count == 0) {
+            return dst - origDst;
+        }
+        if (1 == count) {
+            *dst++ = 0;
+            *dst++ = *src;
+            return dst - origDst;
+        }
+        
+        unsigned value = *src;
+        const uint8_t* s = src + 1;
+        
+        if (*s == value) { // accumulate same values...
+            do {
+                s++;
+                if (s == stop) {
+                    break;
+                }
+            } while (*s == value);
+            dst = flush_same8(dst, value, s - src);
+        } else {    // accumulate diff values...
+            do {
+                if (++s == stop) {
+                    goto FLUSH_DIFF;
+                }
+                // only stop if we hit 3 in a row,
+                // otherwise we get bigger than compuatemax
+            } while (*s != s[-1] || s[-1] != s[-2]);
+            s -= 2; // back up so we don't grab the "same" values that follow
+        FLUSH_DIFF:
+            dst = flush_diff8(dst, src, s - src);
+        }
+        src = s;
+    }
+}
+
+#include "SkUtils.h"
+
+int SkPackBits::Unpack16(const uint8_t* SK_RESTRICT src, size_t srcSize,
+                         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)
+            n += 1;
+            sk_memset16(dst, (src[0] << 8) | src[1], n);
+            src += 2;
+        } else {    // same count (n - 127)
+            n -= 127;
+            PB_MEMCPY(dst, src, n * sizeof(uint16_t));
+            src += n * sizeof(uint16_t);
+        }
+        dst += n;
+    }
+    SkASSERT(src == stop);
+    return dst - origDst;
+}
+
+int SkPackBits::Unpack8(const uint8_t* SK_RESTRICT src, size_t srcSize,
+                        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)
+            n += 1;
+            PB_MEMSET(dst, *src++, n);
+        } else {    // same count (n - 127)
+            n -= 127;
+            PB_MEMCPY(dst, src, n);
+            src += n;
+        }
+        dst += n;
+    }
+    SkASSERT(src == stop);
+    return dst - origDst;
+}
+
+enum UnpackState {
+    CLEAN_STATE,
+    REPEAT_BYTE_STATE,
+    COPY_SRC_STATE
+};
+
+void SkPackBits::Unpack8(uint8_t* SK_RESTRICT dst, size_t dstSkip,
+                         size_t dstWrite, const uint8_t* SK_RESTRICT src) {
+    if (dstWrite == 0) {
+        return;
+    }
+
+    UnpackState state = CLEAN_STATE;
+    size_t      stateCount = 0;
+    
+    // state 1: do the skip-loop
+    while (dstSkip > 0) {
+        unsigned n = *src++;
+        if (n <= 127) {   // repeat count (n + 1)
+            n += 1;
+            if (n > dstSkip) {
+                state = REPEAT_BYTE_STATE;
+                stateCount = n - dstSkip;
+                n = dstSkip;
+                // we don't increment src here, since its needed in stage 2
+            } else {
+                src++;  // skip the src byte
+            }
+        } else {    // same count (n - 127)
+            n -= 127;
+            if (n > dstSkip) {
+                state = COPY_SRC_STATE;
+                stateCount = n - dstSkip;
+                n = dstSkip;
+            }
+            src += n;
+        }
+        dstSkip -= n;
+    }
+    
+    // stage 2: perform any catchup from the skip-stage
+    if (stateCount > dstWrite) {
+        stateCount = dstWrite;
+    }
+    switch (state) {
+        case REPEAT_BYTE_STATE:
+            SkASSERT(stateCount > 0);
+            register_memset_count(stateCount);
+            PB_MEMSET(dst, *src++, stateCount);
+            break;
+        case COPY_SRC_STATE:
+            SkASSERT(stateCount > 0);
+            register_memcpy_count(stateCount);
+            PB_MEMCPY(dst, src, stateCount);
+            src += stateCount;
+            break;
+        default:
+            SkASSERT(stateCount == 0);
+            break;
+    }
+    dst += stateCount;
+    dstWrite -= stateCount;
+
+    // copy at most dstWrite bytes into dst[]
+    while (dstWrite > 0) {
+        unsigned n = *src++;
+        if (n <= 127) {   // repeat count (n + 1)
+            n += 1;
+            if (n > dstWrite) {
+                n = dstWrite;
+            }
+            register_memset_count(n);
+            PB_MEMSET(dst, *src++, n);
+        } else {    // same count (n - 127)
+            n -= 127;
+            if (n > dstWrite) {
+                n = dstWrite;
+            }
+            register_memcpy_count(n);
+            PB_MEMCPY(dst, src, n);
+            src += n;
+        }
+        dst += n;
+        dstWrite -= n;
+    }
+    SkASSERT(0 == dstWrite);
+}
diff --git a/legacy/src/core/SkPaint.cpp b/legacy/src/core/SkPaint.cpp
new file mode 100644
index 0000000..56630ae
--- /dev/null
+++ b/legacy/src/core/SkPaint.cpp
@@ -0,0 +1,2290 @@
+
+/*
+ * 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 <new>
+
+#include "SkPaint.h"
+#include "SkColorFilter.h"
+#include "SkFontHost.h"
+#include "SkImageFilter.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkScalar.h"
+#include "SkScalerContext.h"
+#include "SkStroke.h"
+#include "SkTextFormatParams.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+#include "SkAutoKern.h"
+#include "SkGlyphCache.h"
+#include "SkPaintDefaults.h"
+
+// define this to get a printf for out-of-range parameter in setters
+// e.g. setTextSize(-1)
+//#define SK_REPORT_API_RANGE_CHECK
+
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkLanguage.h"
+#define GEN_ID_INC                  fGenerationID++
+#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
+#else
+#define GEN_ID_INC
+#define GEN_ID_INC_EVAL(expression)
+#endif
+
+SkPaint::SkPaint() {
+    // since we may have padding, we zero everything so that our memcmp() call
+    // in operator== will work correctly.
+    // with this, we can skip 0 and null individual initializations
+    sk_bzero(this, sizeof(*this));
+
+#if 0   // not needed with the bzero call above
+    fTypeface   = NULL;
+    fTextSkewX  = 0;
+    fPathEffect  = NULL;
+    fShader      = NULL;
+    fXfermode    = NULL;
+    fMaskFilter  = NULL;
+    fColorFilter = NULL;
+    fRasterizer  = NULL;
+    fLooper      = NULL;
+    fImageFilter = NULL;
+    fWidth      = 0;
+#endif
+
+    fTextSize   = SkPaintDefaults_TextSize;
+    fTextScaleX = SK_Scalar1;
+    fColor      = SK_ColorBLACK;
+    fMiterLimit = SkPaintDefaults_MiterLimit;
+    fFlags      = SkPaintDefaults_Flags;
+    fCapType    = kDefault_Cap;
+    fJoinType   = kDefault_Join;
+    fTextAlign  = kLeft_Align;
+    fStyle      = kFill_Style;
+    fTextEncoding = kUTF8_TextEncoding;
+    fHinting    = SkPaintDefaults_Hinting;
+#ifdef SK_BUILD_FOR_ANDROID
+    fLanguage = SkLanguage();
+    fFontVariant = kDefault_Variant;
+    fGenerationID = 0;
+#endif
+}
+
+SkPaint::SkPaint(const SkPaint& src) {
+    memcpy(this, &src, sizeof(src));
+
+    SkSafeRef(fTypeface);
+    SkSafeRef(fPathEffect);
+    SkSafeRef(fShader);
+    SkSafeRef(fXfermode);
+    SkSafeRef(fMaskFilter);
+    SkSafeRef(fColorFilter);
+    SkSafeRef(fRasterizer);
+    SkSafeRef(fLooper);
+    SkSafeRef(fImageFilter);
+}
+
+SkPaint::~SkPaint() {
+    SkSafeUnref(fTypeface);
+    SkSafeUnref(fPathEffect);
+    SkSafeUnref(fShader);
+    SkSafeUnref(fXfermode);
+    SkSafeUnref(fMaskFilter);
+    SkSafeUnref(fColorFilter);
+    SkSafeUnref(fRasterizer);
+    SkSafeUnref(fLooper);
+    SkSafeUnref(fImageFilter);
+}
+
+SkPaint& SkPaint::operator=(const SkPaint& src) {
+    SkASSERT(&src);
+
+    SkSafeRef(src.fTypeface);
+    SkSafeRef(src.fPathEffect);
+    SkSafeRef(src.fShader);
+    SkSafeRef(src.fXfermode);
+    SkSafeRef(src.fMaskFilter);
+    SkSafeRef(src.fColorFilter);
+    SkSafeRef(src.fRasterizer);
+    SkSafeRef(src.fLooper);
+    SkSafeRef(src.fImageFilter);
+
+    SkSafeUnref(fTypeface);
+    SkSafeUnref(fPathEffect);
+    SkSafeUnref(fShader);
+    SkSafeUnref(fXfermode);
+    SkSafeUnref(fMaskFilter);
+    SkSafeUnref(fColorFilter);
+    SkSafeUnref(fRasterizer);
+    SkSafeUnref(fLooper);
+    SkSafeUnref(fImageFilter);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    uint32_t oldGenerationID = fGenerationID;
+#endif
+    memcpy(this, &src, sizeof(src));
+#ifdef SK_BUILD_FOR_ANDROID
+    fGenerationID = oldGenerationID + 1;
+#endif
+
+    return *this;
+}
+
+bool operator==(const SkPaint& a, const SkPaint& b) {
+#ifdef SK_BUILD_FOR_ANDROID
+    //assumes that fGenerationID is the last field in the struct
+    return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID));
+#else
+    return !memcmp(&a, &b, sizeof(a));
+#endif
+}
+
+void SkPaint::reset() {
+    SkPaint init;
+
+#ifdef SK_BUILD_FOR_ANDROID
+    uint32_t oldGenerationID = fGenerationID;
+#endif
+    *this = init;
+#ifdef SK_BUILD_FOR_ANDROID
+    fGenerationID = oldGenerationID + 1;
+#endif
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+uint32_t SkPaint::getGenerationID() const {
+    return fGenerationID;
+}
+#endif
+
+#ifdef SK_BUILD_FOR_ANDROID
+unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
+    SkAutoGlyphCache autoCache(*this, NULL);
+    SkGlyphCache* cache = autoCache.getCache();
+    return cache->getBaseGlyphCount(text);
+}
+#endif
+
+void SkPaint::setHinting(Hinting hintingLevel) {
+    GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
+    fHinting = hintingLevel;
+}
+
+void SkPaint::setFlags(uint32_t flags) {
+    GEN_ID_INC_EVAL(fFlags != flags);
+    fFlags = flags;
+}
+
+void SkPaint::setAntiAlias(bool doAA) {
+    GEN_ID_INC_EVAL(doAA != isAntiAlias());
+    this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
+}
+
+void SkPaint::setDither(bool doDither) {
+    GEN_ID_INC_EVAL(doDither != isDither());
+    this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
+}
+
+void SkPaint::setSubpixelText(bool doSubpixel) {
+    GEN_ID_INC_EVAL(doSubpixel != isSubpixelText());
+    this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
+}
+
+void SkPaint::setLCDRenderText(bool doLCDRender) {
+    GEN_ID_INC_EVAL(doLCDRender != isLCDRenderText());
+    this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
+}
+
+void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
+    GEN_ID_INC_EVAL(doEmbeddedBitmapText != isEmbeddedBitmapText());
+    this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
+}
+
+void SkPaint::setAutohinted(bool useAutohinter) {
+    GEN_ID_INC_EVAL(useAutohinter != isAutohinted());
+    this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
+}
+
+void SkPaint::setLinearText(bool doLinearText) {
+    GEN_ID_INC_EVAL(doLinearText != isLinearText());
+    this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
+}
+
+void SkPaint::setVerticalText(bool doVertical) {
+    GEN_ID_INC_EVAL(doVertical != isVerticalText());
+    this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
+}
+
+void SkPaint::setUnderlineText(bool doUnderline) {
+    GEN_ID_INC_EVAL(doUnderline != isUnderlineText());
+    this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
+}
+
+void SkPaint::setStrikeThruText(bool doStrikeThru) {
+    GEN_ID_INC_EVAL(doStrikeThru != isStrikeThruText());
+    this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
+}
+
+void SkPaint::setFakeBoldText(bool doFakeBold) {
+    GEN_ID_INC_EVAL(doFakeBold != isFakeBoldText());
+    this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
+}
+
+void SkPaint::setDevKernText(bool doDevKern) {
+    GEN_ID_INC_EVAL(doDevKern != isDevKernText());
+    this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
+}
+
+void SkPaint::setFilterBitmap(bool doFilter) {
+    GEN_ID_INC_EVAL(doFilter != isFilterBitmap());
+    this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
+}
+
+void SkPaint::setStyle(Style style) {
+    if ((unsigned)style < kStyleCount) {
+        GEN_ID_INC_EVAL((unsigned)style != fStyle);
+        fStyle = style;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
+#endif
+    }
+}
+
+void SkPaint::setColor(SkColor color) {
+    GEN_ID_INC_EVAL(color != fColor);
+    fColor = color;
+}
+
+void SkPaint::setAlpha(U8CPU a) {
+    this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
+                                  SkColorGetG(fColor), SkColorGetB(fColor)));
+}
+
+void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    this->setColor(SkColorSetARGB(a, r, g, b));
+}
+
+void SkPaint::setStrokeWidth(SkScalar width) {
+    if (width >= 0) {
+        GEN_ID_INC_EVAL(width != fWidth);
+        fWidth = width;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
+#endif
+    }
+}
+
+void SkPaint::setStrokeMiter(SkScalar limit) {
+    if (limit >= 0) {
+        GEN_ID_INC_EVAL(limit != fMiterLimit);
+        fMiterLimit = limit;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
+#endif
+    }
+}
+
+void SkPaint::setStrokeCap(Cap ct) {
+    if ((unsigned)ct < kCapCount) {
+        GEN_ID_INC_EVAL((unsigned)ct != fCapType);
+        fCapType = SkToU8(ct);
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
+#endif
+    }
+}
+
+void SkPaint::setStrokeJoin(Join jt) {
+    if ((unsigned)jt < kJoinCount) {
+        GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
+        fJoinType = SkToU8(jt);
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
+#endif
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPaint::setTextAlign(Align align) {
+    if ((unsigned)align < kAlignCount) {
+        GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
+        fTextAlign = SkToU8(align);
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
+#endif
+    }
+}
+
+void SkPaint::setTextSize(SkScalar ts) {
+    if (ts >= 0) {
+        GEN_ID_INC_EVAL(ts != fTextSize);
+        fTextSize = ts;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setTextSize() called with negative value\n");
+#endif
+    }
+}
+
+void SkPaint::setTextScaleX(SkScalar scaleX) {
+    GEN_ID_INC_EVAL(scaleX != fTextScaleX);
+    fTextScaleX = scaleX;
+}
+
+void SkPaint::setTextSkewX(SkScalar skewX) {
+    GEN_ID_INC_EVAL(skewX != fTextSkewX);
+    fTextSkewX = skewX;
+}
+
+void SkPaint::setTextEncoding(TextEncoding encoding) {
+    if ((unsigned)encoding <= kGlyphID_TextEncoding) {
+        GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
+        fTextEncoding = encoding;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
+#endif
+    }
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+void SkPaint::setLanguage(const SkLanguage& language) {
+    if(fLanguage != language) {
+        fLanguage = language;
+        GEN_ID_INC;
+    }
+}
+
+void SkPaint::setFontVariant(FontVariant fontVariant) {
+    if ((unsigned)fontVariant <= kLast_Variant) {
+        GEN_ID_INC_EVAL((unsigned)fontVariant != fFontVariant);
+        fFontVariant = fontVariant;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setFontVariant(%d) out of range\n", fontVariant);
+#endif
+    }
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
+    SkRefCnt_SafeAssign(fTypeface, font);
+    GEN_ID_INC;
+    return font;
+}
+
+SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
+    SkRefCnt_SafeAssign(fRasterizer, r);
+    GEN_ID_INC;
+    return r;
+}
+
+SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
+    SkRefCnt_SafeAssign(fLooper, looper);
+    GEN_ID_INC;
+    return looper;
+}
+
+SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
+    SkRefCnt_SafeAssign(fImageFilter, imageFilter);
+    GEN_ID_INC;
+    return imageFilter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+
+static void DetachDescProc(const SkDescriptor* desc, void* context) {
+    *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text, const SkMatrix* deviceMatrix) {
+    SkGlyphCache* cache;
+    descriptorProc(deviceMatrix, DetachDescProc, &cache, true);
+
+    const SkGlyph& glyph = cache->getUnicharMetrics(text);
+
+    SkGlyphCache::AttachCache(cache);
+    return glyph;
+}
+
+const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId, const SkMatrix* deviceMatrix) {
+    SkGlyphCache* cache;
+    descriptorProc(deviceMatrix, DetachDescProc, &cache, true);
+
+    const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
+
+    SkGlyphCache::AttachCache(cache);
+    return glyph;
+}
+
+const void* SkPaint::findImage(const SkGlyph& glyph, const SkMatrix* deviceMatrix) {
+    // See ::detachCache()
+    SkGlyphCache* cache;
+    descriptorProc(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,
+                          uint16_t glyphs[]) const {
+    if (byteLength == 0) {
+        return 0;
+    }
+
+    SkASSERT(textData != NULL);
+
+    if (NULL == glyphs) {
+        switch (this->getTextEncoding()) {
+        case kUTF8_TextEncoding:
+            return SkUTF8_CountUnichars((const char*)textData, byteLength);
+        case kUTF16_TextEncoding:
+            return SkUTF16_CountUnichars((const uint16_t*)textData,
+                                         byteLength >> 1);
+        case kGlyphID_TextEncoding:
+            return byteLength >> 1;
+        default:
+            SkDEBUGFAIL("unknown text encoding");
+        }
+        return 0;
+    }
+
+    // if we get here, we have a valid glyphs[] array, so time to fill it in
+
+    // handle this encoding before the setup for the glyphcache
+    if (this->getTextEncoding() == kGlyphID_TextEncoding) {
+        // we want to ignore the low bit of byteLength
+        memcpy(glyphs, textData, byteLength >> 1 << 1);
+        return byteLength >> 1;
+    }
+
+    SkAutoGlyphCache autoCache(*this, NULL);
+    SkGlyphCache*    cache = autoCache.getCache();
+
+    const char* text = (const char*)textData;
+    const char* stop = text + byteLength;
+    uint16_t*   gptr = glyphs;
+
+    switch (this->getTextEncoding()) {
+        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;
+}
+
+bool SkPaint::containsText(const void* textData, size_t byteLength) const {
+    if (0 == byteLength) {
+        return true;
+    }
+
+    SkASSERT(textData != NULL);
+
+    // handle this encoding before the setup for the glyphcache
+    if (this->getTextEncoding() == kGlyphID_TextEncoding) {
+        const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
+        size_t count = byteLength >> 1;
+        for (size_t i = 0; i < count; i++) {
+            if (0 == glyphID[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    SkAutoGlyphCache autoCache(*this, NULL);
+    SkGlyphCache*    cache = autoCache.getCache();
+
+    switch (this->getTextEncoding()) {
+        case SkPaint::kUTF8_TextEncoding: {
+            const char* text = static_cast<const char*>(textData);
+            const char* stop = text + byteLength;
+            while (text < stop) {
+                if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
+                    return false;
+                }
+            }
+            break;
+        }
+        case SkPaint::kUTF16_TextEncoding: {
+            const uint16_t* text = static_cast<const uint16_t*>(textData);
+            const uint16_t* stop = text + (byteLength >> 1);
+            while (text < stop) {
+                if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
+                    return false;
+                }
+            }
+            break;
+        }
+        default:
+            SkDEBUGFAIL("unknown text encoding");
+            return false;
+    }
+    return true;
+}
+
+void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
+                               SkUnichar textData[]) const {
+    if (count <= 0) {
+        return;
+    }
+
+    SkASSERT(glyphs != NULL);
+    SkASSERT(textData != NULL);
+
+    SkAutoGlyphCache autoCache(*this, NULL);
+    SkGlyphCache*    cache = autoCache.getCache();
+
+    for (int index = 0; index < count; index++) {
+        textData[index] = cache->glyphToUnichar(glyphs[index]);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
+                                              const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
+                                              const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const uint16_t* ptr = *(const uint16_t**)text;
+    ptr -= 1;
+    unsigned glyphID = *ptr;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
+                                              const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
+                                              const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDAdvance(glyphID);
+}
+
+static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const uint16_t* ptr = *(const uint16_t**)text;
+    ptr -= 1;
+    unsigned glyphID = *ptr;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDAdvance(glyphID);
+}
+
+SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
+                                                bool needFullMetrics) const {
+    static const SkMeasureCacheProc gMeasureCacheProcs[] = {
+        sk_getMetrics_utf8_next,
+        sk_getMetrics_utf16_next,
+        sk_getMetrics_glyph_next,
+
+        sk_getMetrics_utf8_prev,
+        sk_getMetrics_utf16_prev,
+        sk_getMetrics_glyph_prev,
+
+        sk_getAdvance_utf8_next,
+        sk_getAdvance_utf16_next,
+        sk_getAdvance_glyph_next,
+
+        sk_getAdvance_utf8_prev,
+        sk_getAdvance_utf16_prev,
+        sk_getAdvance_glyph_prev
+    };
+
+    unsigned index = this->getTextEncoding();
+
+    if (kBackward_TextBufferDirection == tbd) {
+        index += 3;
+    }
+    if (!needFullMetrics && !this->isDevKernText()) {
+        index += 6;
+    }
+
+    SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
+    return gMeasureCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
+                                        const char** text, SkFixed, SkFixed) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
+                                    const char** text, SkFixed x, SkFixed y) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
+}
+
+static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
+                                        const char** text, SkFixed, SkFixed) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
+                                     const char** text, SkFixed x, SkFixed y) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
+                                    x, y);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
+                                         const char** text, SkFixed, SkFixed) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
+                                     const char** text, SkFixed x, SkFixed y) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID, x, y);
+}
+
+SkDrawCacheProc SkPaint::getDrawCacheProc() const {
+    static const SkDrawCacheProc gDrawCacheProcs[] = {
+        sk_getMetrics_utf8_00,
+        sk_getMetrics_utf16_00,
+        sk_getMetrics_glyph_00,
+
+        sk_getMetrics_utf8_xy,
+        sk_getMetrics_utf16_xy,
+        sk_getMetrics_glyph_xy
+    };
+
+    unsigned index = this->getTextEncoding();
+    if (fFlags & kSubpixelText_Flag) {
+        index += 3;
+    }
+
+    SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
+    return gDrawCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoRestorePaintTextSizeAndFrame {
+public:
+    SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
+            : fPaint((SkPaint*)paint) {
+        fTextSize = paint->getTextSize();
+        fStyle = paint->getStyle();
+        fPaint->setStyle(SkPaint::kFill_Style);
+    }
+
+    ~SkAutoRestorePaintTextSizeAndFrame() {
+        fPaint->setStyle(fStyle);
+        fPaint->setTextSize(fTextSize);
+    }
+
+private:
+    SkPaint*        fPaint;
+    SkScalar        fTextSize;
+    SkPaint::Style  fStyle;
+};
+
+static void set_bounds(const SkGlyph& g, SkRect* bounds) {
+    bounds->set(SkIntToScalar(g.fLeft),
+                SkIntToScalar(g.fTop),
+                SkIntToScalar(g.fLeft + g.fWidth),
+                SkIntToScalar(g.fTop + g.fHeight));
+}
+
+// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
+// we don't overflow along the way
+typedef int64_t Sk48Dot16;
+
+#ifdef SK_SCALAR_IS_FLOAT
+    static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
+        return (float) (x * 1.5258789e-5);   // x * (1 / 65536.0f)
+    }
+#else
+    static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
+        // just return the low 32bits
+        return static_cast<SkFixed>(x);
+    }
+#endif
+
+static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
+    SkScalar sx = Sk48Dot16ToScalar(dx);
+    bounds->join(SkIntToScalar(g.fLeft) + sx,
+                 SkIntToScalar(g.fTop),
+                 SkIntToScalar(g.fLeft + g.fWidth) + sx,
+                 SkIntToScalar(g.fTop + g.fHeight));
+}
+
+static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
+    SkScalar sy = Sk48Dot16ToScalar(dy);
+    bounds->join(SkIntToScalar(g.fLeft),
+                 SkIntToScalar(g.fTop) + sy,
+                 SkIntToScalar(g.fLeft + g.fWidth),
+                 SkIntToScalar(g.fTop + g.fHeight) + sy);
+}
+
+typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
+
+// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
+static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
+    SkASSERT(0 == xyIndex || 1 == xyIndex);
+    return (&glyph.fAdvanceX)[xyIndex];
+}
+
+SkScalar SkPaint::measure_text(SkGlyphCache* cache,
+                               const char* text, size_t byteLength,
+                               int* count, SkRect* bounds) const {
+    SkASSERT(count);
+    if (byteLength == 0) {
+        *count = 0;
+        if (bounds) {
+            bounds->setEmpty();
+        }
+        return 0;
+    }
+
+    SkMeasureCacheProc glyphCacheProc;
+    glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+                                               NULL != bounds);
+
+    int xyIndex;
+    JoinBoundsProc joinBoundsProc;
+    if (this->isVerticalText()) {
+        xyIndex = 1;
+        joinBoundsProc = join_bounds_y;
+    } else {
+        xyIndex = 0;
+        joinBoundsProc = join_bounds_x;
+    }
+
+    int         n = 1;
+    const char* stop = (const char*)text + byteLength;
+    const SkGlyph* g = &glyphCacheProc(cache, &text);
+    // our accumulated fixed-point advances might overflow 16.16, so we use
+    // a 48.16 (64bit) accumulator, and then convert that to scalar at the
+    // very end.
+    Sk48Dot16 x = advance(*g, xyIndex);
+
+    SkAutoKern  autokern;
+
+    if (NULL == bounds) {
+        if (this->isDevKernText()) {
+            int rsb;
+            for (; text < stop; n++) {
+                rsb = g->fRsbDelta;
+                g = &glyphCacheProc(cache, &text);
+                x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
+            }
+        } else {
+            for (; text < stop; n++) {
+                x += advance(glyphCacheProc(cache, &text), xyIndex);
+            }
+        }
+    } else {
+        set_bounds(*g, bounds);
+        if (this->isDevKernText()) {
+            int rsb;
+            for (; text < stop; n++) {
+                rsb = g->fRsbDelta;
+                g = &glyphCacheProc(cache, &text);
+                x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
+                joinBoundsProc(*g, bounds, x);
+                x += advance(*g, xyIndex);
+            }
+        } else {
+            for (; text < stop; n++) {
+                g = &glyphCacheProc(cache, &text);
+                joinBoundsProc(*g, bounds, x);
+                x += advance(*g, xyIndex);
+            }
+        }
+    }
+    SkASSERT(text == stop);
+
+    *count = n;
+    return Sk48Dot16ToScalar(x);
+}
+
+SkScalar SkPaint::measureText(const void* textData, size_t length,
+                              SkRect* bounds, SkScalar zoom) const {
+    const char* text = (const char*)textData;
+    SkASSERT(text != NULL || length == 0);
+
+    SkScalar                            scale = 0;
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+
+    if (this->isLinearText()) {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+
+    SkMatrix zoomMatrix, *zoomPtr = NULL;
+    if (zoom) {
+        zoomMatrix.setScale(zoom, zoom);
+        zoomPtr = &zoomMatrix;
+    }
+
+    SkAutoGlyphCache    autoCache(*this, zoomPtr);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    SkScalar width = 0;
+
+    if (length > 0) {
+        int tempCount;
+
+        width = this->measure_text(cache, text, length, &tempCount, bounds);
+        if (scale) {
+            width = SkScalarMul(width, scale);
+            if (bounds) {
+                bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
+                bounds->fTop = SkScalarMul(bounds->fTop, scale);
+                bounds->fRight = SkScalarMul(bounds->fRight, scale);
+                bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
+            }
+        }
+    }
+    return width;
+}
+
+typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
+
+static bool forward_textBufferPred(const char* text, const char* stop) {
+    return text < stop;
+}
+
+static bool backward_textBufferPred(const char* text, const char* stop) {
+    return text > stop;
+}
+
+static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
+                                             const char** text, size_t length,
+                                             const char** stop) {
+    if (SkPaint::kForward_TextBufferDirection == tbd) {
+        *stop = *text + length;
+        return forward_textBufferPred;
+    } else {
+        // text should point to the end of the buffer, and stop to the beginning
+        *stop = *text;
+        *text += length;
+        return backward_textBufferPred;
+    }
+}
+
+size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
+                          SkScalar* measuredWidth,
+                          TextBufferDirection tbd) const {
+    if (0 == length || 0 >= maxWidth) {
+        if (measuredWidth) {
+            *measuredWidth = 0;
+        }
+        return 0;
+    }
+
+    if (0 == fTextSize) {
+        if (measuredWidth) {
+            *measuredWidth = 0;
+        }
+        return length;
+    }
+
+    SkASSERT(textD != NULL);
+    const char* text = (const char*)textD;
+
+    SkScalar                            scale = 0;
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+
+    if (this->isLinearText()) {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize);
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+
+    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
+    const char*      stop;
+    SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
+    const int        xyIndex = this->isVerticalText() ? 1 : 0;
+    // use 64bits for our accumulator, to avoid overflowing 16.16
+    Sk48Dot16        max = SkScalarToFixed(maxWidth);
+    Sk48Dot16        width = 0;
+
+    SkAutoKern  autokern;
+
+    if (this->isDevKernText()) {
+        int rsb = 0;
+        while (pred(text, stop)) {
+            const char* curr = text;
+            const SkGlyph& g = glyphCacheProc(cache, &text);
+            SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
+            if ((width += x) > max) {
+                width -= x;
+                text = curr;
+                break;
+            }
+            rsb = g.fRsbDelta;
+        }
+    } else {
+        while (pred(text, stop)) {
+            const char* curr = text;
+            SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
+            if ((width += x) > max) {
+                width -= x;
+                text = curr;
+                break;
+            }
+        }
+    }
+
+    if (measuredWidth) {
+        SkScalar scalarWidth = Sk48Dot16ToScalar(width);
+        if (scale) {
+            scalarWidth = SkScalarMul(scalarWidth, scale);
+        }
+        *measuredWidth = scalarWidth;
+    }
+
+    // return the number of bytes measured
+    return (kForward_TextBufferDirection == tbd) ?
+                text - stop + length : stop - text + length;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
+    *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
+    return false;   // don't detach the cache
+}
+
+static void FontMetricsDescProc(const SkDescriptor* desc, void* context) {
+    SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
+}
+
+SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
+    SkScalar                            scale = 0;
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+
+    if (this->isLinearText()) {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+
+    SkMatrix zoomMatrix, *zoomPtr = NULL;
+    if (zoom) {
+        zoomMatrix.setScale(zoom, zoom);
+        zoomPtr = &zoomMatrix;
+    }
+
+#if 0
+    SkAutoGlyphCache    autoCache(*this, zoomPtr);
+    SkGlyphCache*       cache = autoCache.getCache();
+    const FontMetrics&  my = cache->getFontMetricsY();
+#endif
+    FontMetrics storage;
+    if (NULL == metrics) {
+        metrics = &storage;
+    }
+
+    this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics, true);
+
+    if (scale) {
+        metrics->fTop = SkScalarMul(metrics->fTop, scale);
+        metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
+        metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
+        metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
+        metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
+    }
+    return metrics->fDescent - metrics->fAscent + metrics->fLeading;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
+    bounds->set(g.fLeft * scale,
+                g.fTop * scale,
+                (g.fLeft + g.fWidth) * scale,
+                (g.fTop + g.fHeight) * scale);
+}
+
+int SkPaint::getTextWidths(const void* textData, size_t byteLength,
+                           SkScalar widths[], SkRect bounds[]) const {
+    if (0 == byteLength) {
+        return 0;
+    }
+
+    SkASSERT(NULL != textData);
+
+    if (NULL == widths && NULL == bounds) {
+        return this->countText(textData, byteLength);
+    }
+
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+    SkScalar                            scale = 0;
+
+    if (this->isLinearText()) {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+
+    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkGlyphCache*       cache = autoCache.getCache();
+    SkMeasureCacheProc  glyphCacheProc;
+    glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+                                               NULL != bounds);
+
+    const char* text = (const char*)textData;
+    const char* stop = text + byteLength;
+    int         count = 0;
+    const int   xyIndex = this->isVerticalText() ? 1 : 0;
+
+    if (this->isDevKernText()) {
+        // we adjust the widths returned here through auto-kerning
+        SkAutoKern  autokern;
+        SkFixed     prevWidth = 0;
+
+        if (scale) {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    SkFixed  adjust = autokern.adjust(g);
+
+                    if (count > 0) {
+                        SkScalar w = SkFixedToScalar(prevWidth + adjust);
+                        *widths++ = SkScalarMul(w, scale);
+                    }
+                    prevWidth = advance(g, xyIndex);
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++, scale);
+                }
+                ++count;
+            }
+            if (count > 0 && widths) {
+                *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
+            }
+        } else {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    SkFixed  adjust = autokern.adjust(g);
+
+                    if (count > 0) {
+                        *widths++ = SkFixedToScalar(prevWidth + adjust);
+                    }
+                    prevWidth = advance(g, xyIndex);
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++);
+                }
+                ++count;
+            }
+            if (count > 0 && widths) {
+                *widths = SkFixedToScalar(prevWidth);
+            }
+        }
+    } else {    // no devkern
+        if (scale) {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
+                                            scale);
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++, scale);
+                }
+                ++count;
+            }
+        } else {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    *widths++ = SkFixedToScalar(advance(g, xyIndex));
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++);
+                }
+                ++count;
+            }
+        }
+    }
+
+    SkASSERT(text == stop);
+    return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkDraw.h"
+
+void SkPaint::getTextPath(const void* textData, size_t length,
+                          SkScalar x, SkScalar y, 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, true);
+    SkMatrix            matrix;
+    SkScalar            prevXPos = 0;
+
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+    path->reset();
+
+    SkScalar        xpos;
+    const SkPath*   iterPath;
+    while ((iterPath = iter.next(&xpos)) != NULL) {
+        matrix.postTranslate(xpos - prevXPos, 0);
+        path->addPath(*iterPath, matrix);
+        prevXPos = xpos;
+    }
+}
+
+static void add_flattenable(SkDescriptor* desc, uint32_t tag,
+                            SkFlattenableWriteBuffer* buffer) {
+    buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
+}
+
+// SkFontHost can override this choice in FilterRec()
+static SkMask::Format computeMaskFormat(const SkPaint& paint) {
+    uint32_t flags = paint.getFlags();
+
+    // Antialiasing being disabled trumps all other settings.
+    if (!(flags & SkPaint::kAntiAlias_Flag)) {
+        return SkMask::kBW_Format;
+    }
+
+    if (flags & SkPaint::kLCDRenderText_Flag) {
+        return SkMask::kLCD16_Format;
+    }
+
+    return SkMask::kA8_Format;
+}
+
+// if linear-text is on, then we force hinting to be off (since that's sort of
+// the point of linear-text.
+static SkPaint::Hinting computeHinting(const SkPaint& paint) {
+    SkPaint::Hinting h = paint.getHinting();
+    if (paint.isLinearText()) {
+        h = SkPaint::kNo_Hinting;
+    }
+    return h;
+}
+
+// return true if the paint is just a single color (i.e. not a shader). If its
+// a shader, then we can't compute a const luminance for it :(
+static bool justAColor(const SkPaint& paint, SkColor* color) {
+    if (paint.getShader()) {
+        return false;
+    }
+    SkColor c = paint.getColor();
+    if (paint.getColorFilter()) {
+        c = paint.getColorFilter()->filterColor(c);
+    }
+    if (color) {
+        *color = c;
+    }
+    return true;
+}
+
+#ifdef SK_USE_COLOR_LUMINANCE
+static SkColor computeLuminanceColor(const SkPaint& paint) {
+    SkColor c;
+    if (!justAColor(paint, &c)) {
+        c = SkColorSetRGB(0x7F, 0x80, 0x7F);
+    }
+    return c;
+}
+
+#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
+    #define SK_MAX_SIZE_FOR_LCDTEXT    48
+#endif
+
+static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
+    SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
+                    SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
+    SkScalar size = SkScalarMul(area, rec.fTextSize);
+    return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
+}
+
+/*
+ *  Return the scalar with only limited fractional precision. Used to consolidate matrices
+ *  that vary only slightly when we create our key into the font cache, since the font scaler
+ *  typically returns the same looking resuts for tiny changes in the matrix.
+ */
+static SkScalar sk_relax(SkScalar x) {
+#ifdef SK_SCALAR_IS_FLOAT
+    int n = sk_float_round2int(x * 1024);
+    return n / 1024.0f;
+#else
+    // round to the nearest 10 fractional bits
+    return (x + (1 << 5)) & ~(1024 - 1);
+#endif
+}
+
+void SkScalerContext::MakeRec(const SkPaint& paint,
+                              const SkMatrix* deviceMatrix, Rec* rec) {
+    SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
+
+    SkTypeface* typeface = paint.getTypeface();
+    rec->fOrigFontID = SkTypeface::UniqueID(typeface);
+    rec->fFontID = rec->fOrigFontID;
+    rec->fTextSize = paint.getTextSize();
+    rec->fPreScaleX = paint.getTextScaleX();
+    rec->fPreSkewX  = paint.getTextSkewX();
+
+    if (deviceMatrix) {
+        rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
+        rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
+        rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
+        rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
+    } else {
+        rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
+        rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
+    }
+
+    SkPaint::Style  style = paint.getStyle();
+    SkScalar        strokeWidth = paint.getStrokeWidth();
+
+    unsigned flags = 0;
+
+#ifdef SK_USE_FREETYPE_EMBOLDEN
+    // It is possible that the SkTypeface used to draw glyphs has
+    // different properties than the SkTypeface set in the SkPaint.
+    // If we are asked to render bold text with a bold font, and are
+    // forced to fall back to a font with normal weight for some
+    // glyphs, we need to use fake bold to render those glyphs. In
+    // order to do that, we set SkScalerContext's "embolden" flag
+    // here if we are trying to draw bold text via any means, and
+    // ignore it at the glyph outline generation stage if the font
+    // actually being used is already bold.
+    if (paint.isFakeBoldText() || (typeface && typeface->isBold())) {
+        flags |= SkScalerContext::kEmbolden_Flag;
+    }
+#else
+    if (paint.isFakeBoldText()) {
+        SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
+                                                    kStdFakeBoldInterpKeys,
+                                                    kStdFakeBoldInterpValues,
+                                                    kStdFakeBoldInterpLength);
+        SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
+
+        if (style == SkPaint::kFill_Style) {
+            style = SkPaint::kStrokeAndFill_Style;
+            strokeWidth = extra;    // ignore paint's strokeWidth if it was "fill"
+        } else {
+            strokeWidth += extra;
+        }
+    }
+#endif
+
+    if (paint.isDevKernText()) {
+        flags |= SkScalerContext::kDevKernText_Flag;
+    }
+
+    if (style != SkPaint::kFill_Style && strokeWidth > 0) {
+        rec->fFrameWidth = strokeWidth;
+        rec->fMiterLimit = paint.getStrokeMiter();
+        rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
+
+        if (style == SkPaint::kStrokeAndFill_Style) {
+            flags |= SkScalerContext::kFrameAndFill_Flag;
+        }
+    } else {
+        rec->fFrameWidth = 0;
+        rec->fMiterLimit = 0;
+        rec->fStrokeJoin = 0;
+    }
+
+    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)) {
+            // eeek, can't support LCD
+            rec->fMaskFormat = SkMask::kA8_Format;
+        } else {
+            if (SkFontHost::kVertical_LCDOrientation == orient) {
+                flags |= SkScalerContext::kLCD_Vertical_Flag;
+            }
+            if (SkFontHost::kBGR_LCDOrder == order) {
+                flags |= SkScalerContext::kLCD_BGROrder_Flag;
+            }
+        }
+    }
+
+    if (paint.isEmbeddedBitmapText()) {
+        flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
+    }
+    if (paint.isSubpixelText()) {
+        flags |= SkScalerContext::kSubpixelPositioning_Flag;
+    }
+    if (paint.isAutohinted()) {
+        flags |= SkScalerContext::kAutohinting_Flag;
+    }
+    if (paint.isVerticalText()) {
+        flags |= SkScalerContext::kVertical_Flag;
+    }
+    if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
+        flags |= SkScalerContext::kGenA8FromLCD_Flag;
+    }
+    rec->fFlags = SkToU16(flags);
+
+    // these modify fFlags, so do them after assigning fFlags
+    rec->setHinting(computeHinting(paint));
+#ifdef SK_USE_COLOR_LUMINANCE
+    rec->setLuminanceColor(computeLuminanceColor(paint));
+#else
+    rec->setLuminanceBits(computeLuminance(paint));
+#endif
+#ifdef SK_BUILD_FOR_ANDROID
+    rec->fLanguage = paint.getLanguage();
+    rec->fFontVariant = paint.getFontVariant();
+#endif //SK_BUILD_FOR_ANDROID
+
+    /*  Allow the fonthost to modify our rec before we use it as a key into the
+        cache. This way if we're asking for something that they will ignore,
+        they can modify our rec up front, so we don't create duplicate cache
+        entries.
+     */
+    SkFontHost::FilterRec(rec);
+
+    // be sure to call PostMakeRec(rec) before you actually use it!
+}
+
+/**
+ *  We ensure that the rec is self-consistent and efficient (where possible)
+ */
+void SkScalerContext::PostMakeRec(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.
+     */
+    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
+            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());
+            // reduce to our finite number of bits
+            lum = reduce_lumbits(lum);
+            rec->setLuminanceColor(SkColorSetRGB(lum, lum, lum));
+#endif
+            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
+            break;
+    }
+}
+
+#define MIN_SIZE_FOR_EFFECT_BUFFER  1024
+
+#ifdef SK_DEBUG
+    #define TEST_DESC
+#endif
+
+/*
+ *  ignoreGamma tells us that the caller just wants metrics that are unaffected
+ *  by gamma correction, so we jam the luminance field to 0 (most common value
+ *  for black text) in hopes that we get a cache hit easier. A better solution
+ *  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 (*proc)(const SkDescriptor*, void*),
+                             void* context, bool ignoreGamma) const {
+    SkScalerContext::Rec    rec;
+
+    SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
+    if (ignoreGamma) {
+#ifdef SK_USE_COLOR_LUMINANCE
+        rec.setLuminanceColor(0);
+#else
+        rec.setLuminanceBits(0);
+#endif
+    }
+
+    size_t          descSize = sizeof(rec);
+    int             entryCount = 1;
+    SkPathEffect*   pe = this->getPathEffect();
+    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);
+
+    if (pe) {
+        peBuffer.writeFlattenable(pe);
+        descSize += peBuffer.size();
+        entryCount += 1;
+        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing when we do the scan conversion
+        // seems like we could support kLCD as well at this point...
+    }
+    if (mf) {
+        mfBuffer.writeFlattenable(mf);
+        descSize += mfBuffer.size();
+        entryCount += 1;
+        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing with maskfilters
+    }
+    if (ra) {
+        raBuffer.writeFlattenable(ra);
+        descSize += raBuffer.size();
+        entryCount += 1;
+        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing when we do the scan conversion
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Now that we're done tweaking the rec, call the PostMakeRec cleanup
+    SkScalerContext::PostMakeRec(&rec);
+    
+    descSize += SkDescriptor::ComputeOverhead(entryCount);
+
+    SkAutoDescriptor    ad(descSize);
+    SkDescriptor*       desc = ad.getDesc();
+
+    desc->init();
+    desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+    if (pe) {
+        add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
+    }
+    if (mf) {
+        add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
+    }
+    if (ra) {
+        add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
+    }
+
+    SkASSERT(descSize == desc->getLength());
+    desc->computeChecksum();
+
+#ifdef TEST_DESC
+    {
+        // Check that we completely write the bytes in desc (our key), and that
+        // there are no uninitialized bytes. If there were, then we would get
+        // false-misses (or worse, false-hits) in our fontcache.
+        //
+        // We do this buy filling 2 others, one with 0s and the other with 1s
+        // and create those, and then check that all 3 are identical.
+        SkAutoDescriptor    ad1(descSize);
+        SkAutoDescriptor    ad2(descSize);
+        SkDescriptor*       desc1 = ad1.getDesc();
+        SkDescriptor*       desc2 = ad2.getDesc();
+        
+        memset(desc1, 0x00, descSize);
+        memset(desc2, 0xFF, descSize);
+        
+        desc1->init();
+        desc2->init();
+        desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+        desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+        
+        if (pe) {
+            add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
+            add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
+        }
+        if (mf) {
+            add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
+            add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
+        }
+        if (ra) {
+            add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
+            add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
+        }
+        
+        SkASSERT(descSize == desc1->getLength());
+        SkASSERT(descSize == desc2->getLength());
+        desc1->computeChecksum();
+        desc2->computeChecksum();
+        SkASSERT(!memcmp(desc, desc1, descSize));
+        SkASSERT(!memcmp(desc, desc2, descSize));
+    }
+#endif
+    
+    proc(desc, context);
+}
+
+SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
+    SkGlyphCache* cache;
+    this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
+    return cache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+static uintptr_t asint(const void* p) {
+    return reinterpret_cast<uintptr_t>(p);
+}
+
+union Scalar32 {
+    SkScalar    fScalar;
+    uint32_t    f32;
+};
+
+static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
+    SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
+    Scalar32 tmp;
+    tmp.fScalar = value;
+    *ptr = tmp.f32;
+    return ptr + 1;
+}
+
+static SkScalar read_scalar(const uint32_t*& ptr) {
+    SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
+    Scalar32 tmp;
+    tmp.f32 = *ptr++;
+    return tmp.fScalar;
+}
+
+static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
+    SkASSERT(a == (uint8_t)a);
+    SkASSERT(b == (uint8_t)b);
+    SkASSERT(c == (uint8_t)c);
+    SkASSERT(d == (uint8_t)d);
+    return (a << 24) | (b << 16) | (c << 8) | d;
+}
+
+enum FlatFlags {
+    kHasTypeface_FlatFlag   = 0x01,
+    kHasEffects_FlatFlag    = 0x02
+};
+
+// The size of a flat paint's POD fields
+static const uint32_t kPODPaintSize =   5 * sizeof(SkScalar) +
+                                        1 * sizeof(SkColor) +
+                                        1 * sizeof(uint16_t) +
+                                        6 * sizeof(uint8_t);
+
+/*  To save space/time, we analyze the paint, and write a truncated version of
+    it if there are not tricky elements like shaders, etc.
+ */
+void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
+    uint8_t flatFlags = 0;
+    if (this->getTypeface()) {
+        flatFlags |= kHasTypeface_FlatFlag;
+    }
+    if (asint(this->getPathEffect()) |
+        asint(this->getShader()) |
+        asint(this->getXfermode()) |
+        asint(this->getMaskFilter()) |
+        asint(this->getColorFilter()) |
+        asint(this->getRasterizer()) |
+        asint(this->getLooper()) |
+        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());
+
+#ifdef SK_BUILD_FOR_ANDROID
+    buffer.writeInt(this->getFontVariant());
+    const SkString& langTag = this->getLanguage().getTag();
+    buffer.writeString(langTag.c_str(), langTag.size());
+#endif
+
+    // now we're done with ptr and the (pre)reserved space. If we need to write
+    // additional fields, use the buffer directly
+    if (flatFlags & kHasTypeface_FlatFlag) {
+        buffer.writeTypeface(this->getTypeface());
+    }
+    if (flatFlags & kHasEffects_FlatFlag) {
+        buffer.writeFlattenable(this->getPathEffect());
+        buffer.writeFlattenable(this->getShader());
+        buffer.writeFlattenable(this->getXfermode());
+        buffer.writeFlattenable(this->getMaskFilter());
+        buffer.writeFlattenable(this->getColorFilter());
+        buffer.writeFlattenable(this->getRasterizer());
+        buffer.writeFlattenable(this->getLooper());
+        buffer.writeFlattenable(this->getImageFilter());
+    }
+}
+
+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);
+
+    // 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++);
+
+    // previously flags:16, textAlign:8, flatFlags:8
+    // now flags:16, hinting:4, textAlign:4, flatFlags:8
+    uint32_t tmp = *pod++;
+    this->setFlags(tmp >> 16);
+
+    if (buffer.getPictureVersion() == PICTURE_VERSION_ICS) {
+        this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xFF));
+        this->setHinting(SkPaintDefaults_Hinting);
+    } else {
+        // 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;
+
+    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()));
+#endif
+
+    if (flatFlags & kHasTypeface_FlatFlag) {
+        this->setTypeface(buffer.readTypeface());
+    } else {
+        this->setTypeface(NULL);
+    }
+
+    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);
+    } else {
+        this->setPathEffect(NULL);
+        this->setShader(NULL);
+        this->setXfermode(NULL);
+        this->setMaskFilter(NULL);
+        this->setColorFilter(NULL);
+        this->setRasterizer(NULL);
+        this->setLooper(NULL);
+        this->setImageFilter(NULL);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkShader* SkPaint::setShader(SkShader* shader) {
+    GEN_ID_INC_EVAL(shader != fShader);
+    SkRefCnt_SafeAssign(fShader, shader);
+    return shader;
+}
+
+SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
+    GEN_ID_INC_EVAL(filter != fColorFilter);
+    SkRefCnt_SafeAssign(fColorFilter, filter);
+    return filter;
+}
+
+SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
+    GEN_ID_INC_EVAL(mode != fXfermode);
+    SkRefCnt_SafeAssign(fXfermode, mode);
+    return mode;
+}
+
+SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
+    SkSafeUnref(fXfermode);
+    fXfermode = SkXfermode::Create(mode);
+    GEN_ID_INC;
+    return fXfermode;
+}
+
+SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
+    GEN_ID_INC_EVAL(effect != fPathEffect);
+    SkRefCnt_SafeAssign(fPathEffect, effect);
+    return effect;
+}
+
+SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
+    GEN_ID_INC_EVAL(filter != fMaskFilter);
+    SkRefCnt_SafeAssign(fMaskFilter, filter);
+    return filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
+    SkPath          effectPath, strokePath;
+    const SkPath*   path = &src;
+
+    SkScalar width = this->getStrokeWidth();
+
+    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 (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 (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)
+}
+
+const SkRect& SkPaint::doComputeFastBounds(const SkRect& src,
+                                                 SkRect* storage) const {
+    SkASSERT(storage);
+
+    if (this->getLooper()) {
+        SkASSERT(this->getLooper()->canComputeFastBounds(*this));
+        this->getLooper()->computeFastBounds(*this, src, storage);
+        return *storage;
+    }
+
+    if (this->getStyle() != SkPaint::kFill_Style) {
+        // since we're stroked, outset the rect by the radius (and join type)
+        SkScalar radius = SkScalarHalf(this->getStrokeWidth());
+        if (0 == radius) {  // hairline
+            radius = SK_Scalar1;
+        } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
+            SkScalar scale = this->getStrokeMiter();
+            if (scale > SK_Scalar1) {
+                radius = SkScalarMul(radius, scale);
+            }
+        }
+        storage->set(src.fLeft - radius, src.fTop - radius,
+                     src.fRight + radius, src.fBottom + radius);
+    } else {
+        *storage = src;
+    }
+
+    // check the mask filter
+    if (this->getMaskFilter()) {
+        this->getMaskFilter()->computeFastBounds(*storage, storage);
+    }
+
+    return *storage;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool has_thick_frame(const SkPaint& paint) {
+    return  paint.getStrokeWidth() > 0 &&
+            paint.getStyle() != SkPaint::kFill_Style;
+}
+
+SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
+                                    const SkPaint& paint,
+                                    bool applyStrokeAndPathEffects,
+                                    bool forceLinearTextOn) : fPaint(paint) {
+    fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
+                                                true);
+
+    if (forceLinearTextOn) {
+        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
+    if (fPaint.getPathEffect() == NULL) {
+        fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
+        fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
+        if (has_thick_frame(fPaint)) {
+            fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
+        }
+    } else {
+        fScale = SK_Scalar1;
+    }
+
+    if (!applyStrokeAndPathEffects) {
+        fPaint.setStyle(SkPaint::kFill_Style);
+        fPaint.setPathEffect(NULL);
+    }
+
+    fCache = fPaint.detachCache(NULL);
+
+    SkPaint::Style  style = SkPaint::kFill_Style;
+    SkPathEffect*   pe = NULL;
+
+    if (!applyStrokeAndPathEffects) {
+        style = paint.getStyle();   // restore
+        pe = paint.getPathEffect();     // restore
+    }
+    fPaint.setStyle(style);
+    fPaint.setPathEffect(pe);
+    fPaint.setMaskFilter(paint.getMaskFilter());    // restore
+
+    // now compute fXOffset if needed
+
+    SkScalar xOffset = 0;
+    if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
+        int      count;
+        SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
+                                                         &count, NULL), fScale);
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            width = SkScalarHalf(width);
+        }
+        xOffset = -width;
+    }
+    fXPos = xOffset;
+    fPrevAdvance = 0;
+
+    fText = text;
+    fStop = text + length;
+    
+    fXYIndex = paint.isVerticalText() ? 1 : 0;
+}
+
+SkTextToPathIter::~SkTextToPathIter() {
+    SkGlyphCache::AttachCache(fCache);
+}
+
+const SkPath* SkTextToPathIter::next(SkScalar* xpos) {
+    while (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;
+            }
+            return fCache->findPath(glyph);
+        }
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkPaint::nothingToDraw() const {
+    if (fLooper) {
+        return false;
+    }
+    SkXfermode::Mode mode;
+    if (SkXfermode::AsMode(fXfermode, &mode)) {
+        switch (mode) {
+            case SkXfermode::kSrcOver_Mode:
+            case SkXfermode::kSrcATop_Mode:
+            case SkXfermode::kDstOut_Mode:
+            case SkXfermode::kDstOver_Mode:
+            case SkXfermode::kPlus_Mode:
+                return 0 == this->getAlpha();
+            case SkXfermode::kDst_Mode:
+                return true;
+            default:
+                break;
+        }
+    }
+    return false;
+}
+
+
+//////////// 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;
+}
+
+//////
+
+bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
+    SkCanvas canvas;
+
+    this->init(&canvas);
+    for (;;) {
+        SkPaint p(paint);
+        if (this->next(&canvas, &p)) {
+            p.setLooper(NULL);
+            if (!p.canComputeFastBounds()) {
+                return false;
+            }
+        } else {
+            break;
+        }
+    }
+    return true;
+}
+
+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);
+        if (this->next(&canvas, &p)) {
+            SkRect r(src);
+
+            p.setLooper(NULL);
+            p.computeFastBounds(r, &r);
+            canvas.getTotalMatrix().mapRect(&r);
+
+            if (firstTime) {
+                *dst = r;
+            } else {
+                dst->join(r);
+            }
+        } else {
+            break;
+        }
+    }
+}
+
diff --git a/legacy/src/core/SkPaintDefaults.h b/legacy/src/core/SkPaintDefaults.h
new file mode 100644
index 0000000..3ea1cd3
--- /dev/null
+++ b/legacy/src/core/SkPaintDefaults.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 SkPaintDefaults_DEFINED
+#define SkPaintDefaults_DEFINED
+
+#include "SkPaint.h"
+
+/**
+ *  Any of these can be specified by the build system (or SkUserConfig.h)
+ *  to change the default values for a SkPaint. This file should not be
+ *  edited directly.
+ */
+
+#ifndef SkPaintDefaults_Flags
+    #define SkPaintDefaults_Flags           0
+#endif
+
+#ifndef SkPaintDefaults_TextSize
+    #define SkPaintDefaults_TextSize        SkIntToScalar(12)
+#endif
+
+#ifndef SkPaintDefaults_Hinting
+    #define SkPaintDefaults_Hinting         SkPaint::kNormal_Hinting
+#endif
+
+#ifndef SkPaintDefaults_MiterLimit
+    #define SkPaintDefaults_MiterLimit      SkIntToScalar(4)
+#endif
+
+#endif
diff --git a/legacy/src/core/SkPath.cpp b/legacy/src/core/SkPath.cpp
new file mode 100644
index 0000000..3436996
--- /dev/null
+++ b/legacy/src/core/SkPath.cpp
@@ -0,0 +1,2118 @@
+
+/*
+ * 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 "SkPath.h"
+#include "SkReader32.h"
+#include "SkWriter32.h"
+#include "SkMath.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Path.bounds is defined to be the bounds of all the control points.
+ *  If we called bounds.join(r) we would skip r if r was empty, which breaks
+ *  our promise. Hence we have a custom joiner that doesn't look at emptiness
+ */
+static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
+    dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
+    dst->fTop = SkMinScalar(dst->fTop, src.fTop);
+    dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
+    dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
+}
+
+static bool is_degenerate(const SkPath& path) {
+    SkPath::Iter iter(path, false);
+    SkPoint pts[4];
+    return SkPath::kDone_Verb == iter.next(pts);
+}
+
+/*  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).
+
+    It captures some state about the path up front (i.e. if it already has a
+    cached bounds), and the if it can, it updates the cache bounds explicitly,
+    avoiding the need to revisit all of the points in getBounds().
+
+    It also notes if the path was originally degenerate, and if so, sets
+    isConvex to true. Thus it can only be used if the contour being added is
+    convex.
+ */
+class SkAutoPathBoundsUpdate {
+public:
+    SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
+        this->init(path);
+    }
+
+    SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
+                           SkScalar right, SkScalar bottom) {
+        fRect.set(left, top, right, bottom);
+        this->init(path);
+    }
+
+    ~SkAutoPathBoundsUpdate() {
+        fPath->setIsConvex(fDegenerate);
+        if (fEmpty) {
+            fPath->fBounds = fRect;
+            fPath->fBoundsIsDirty = false;
+        } else if (!fDirty) {
+            joinNoEmptyChecks(&fPath->fBounds, fRect);
+            fPath->fBoundsIsDirty = false;
+        }
+    }
+
+private:
+    SkPath* fPath;
+    SkRect  fRect;
+    bool    fDirty;
+    bool    fDegenerate;
+    bool    fEmpty;
+
+    // returns true if we should proceed
+    void init(SkPath* path) {
+        fPath = path;
+        fDirty = SkToBool(path->fBoundsIsDirty);
+        fDegenerate = is_degenerate(*path);
+        fEmpty = path->isEmpty();
+        // Cannot use fRect for our bounds unless we know it is sorted
+        fRect.sort();
+    }
+};
+
+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);
+    } else {
+        bounds->set(pts.begin(), pts.count());
+//        SkDebugf("------- compute bounds %p %d", &pts, pts.count());
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/*
+    Stores the verbs and points as they are given to us, with exceptions:
+    - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
+    - we insert a Move(0,0) if Line | Quad | Cubic is our first command
+
+    The iterator does more cleanup, especially if forceClose == true
+    1. If we encounter degenerate segments, remove them
+    2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
+    3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
+    4. if we encounter Line | Quad | Cubic after Close, cons up a Move
+*/
+
+////////////////////////////////////////////////////////////////////////////
+
+// flag to require a moveTo if we begin with something else, like lineTo etc.
+#define INITIAL_LASTMOVETOINDEX_VALUE   ~0
+
+SkPath::SkPath() 
+    : fFillType(kWinding_FillType)
+    , fBoundsIsDirty(true) {
+    fConvexity = kUnknown_Convexity;
+    fSegmentMask = 0;
+    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+#ifdef SK_BUILD_FOR_ANDROID
+    fGenerationID = 0;
+    fSourcePath = NULL;
+#endif
+}
+
+SkPath::SkPath(const SkPath& src) {
+    SkDEBUGCODE(src.validate();)
+    *this = src;
+#ifdef SK_BUILD_FOR_ANDROID
+    // the assignment operator above increments the ID so correct for that here
+    fGenerationID = src.fGenerationID;
+    fSourcePath = NULL;
+#endif
+}
+
+SkPath::~SkPath() {
+    SkDEBUGCODE(this->validate();)
+}
+
+SkPath& SkPath::operator=(const SkPath& src) {
+    SkDEBUGCODE(src.validate();)
+
+    if (this != &src) {
+        fBounds         = src.fBounds;
+        fPts            = src.fPts;
+        fVerbs          = src.fVerbs;
+        fFillType       = src.fFillType;
+        fBoundsIsDirty  = src.fBoundsIsDirty;
+        fConvexity      = src.fConvexity;
+        fSegmentMask    = src.fSegmentMask;
+        fLastMoveToIndex = src.fLastMoveToIndex;
+        GEN_ID_INC;
+    }
+    SkDEBUGCODE(this->validate();)
+    return *this;
+}
+
+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.
+
+    // We explicitly check fSegmentMask as a quick-reject. We could skip it,
+    // since it is only a cache of info in the fVerbs, but its a fast way to
+    // notice a difference
+
+    return &a == &b ||
+        (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
+         a.fVerbs == b.fVerbs && a.fPts == b.fPts);
+}
+
+void SkPath::swap(SkPath& other) {
+    SkASSERT(&other != NULL);
+
+    if (this != &other) {
+        SkTSwap<SkRect>(fBounds, other.fBounds);
+        fPts.swap(other.fPts);
+        fVerbs.swap(other.fVerbs);
+        SkTSwap<uint8_t>(fFillType, other.fFillType);
+        SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
+        SkTSwap<uint8_t>(fConvexity, other.fConvexity);
+        SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
+        SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex);
+        GEN_ID_INC;
+    }
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+uint32_t SkPath::getGenerationID() const {
+    return fGenerationID;
+}
+
+const SkPath* SkPath::getSourcePath() const {
+    return fSourcePath;
+}
+
+void SkPath::setSourcePath(const SkPath* path) {
+    fSourcePath = path;
+}
+#endif
+
+void SkPath::reset() {
+    SkDEBUGCODE(this->validate();)
+
+    fPts.reset();
+    fVerbs.reset();
+    GEN_ID_INC;
+    fBoundsIsDirty = true;
+    fConvexity = kUnknown_Convexity;
+    fSegmentMask = 0;
+    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+}
+
+void SkPath::rewind() {
+    SkDEBUGCODE(this->validate();)
+
+    fPts.rewind();
+    fVerbs.rewind();
+    GEN_ID_INC;
+    fConvexity = kUnknown_Convexity;
+    fBoundsIsDirty = true;
+    fSegmentMask = 0;
+    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+}
+
+bool SkPath::isEmpty() const {
+    SkDEBUGCODE(this->validate();)
+    return 0 == fVerbs.count();
+}
+
+/*
+ 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
+  2: vertical down
+  3: horizontal left
+ 
+A rectangle cycles up/right/down/left or up/left/down/right.
+
+The test fails if:
+  The path is closed, and followed by a line.
+  A second move creates a new endpoint.
+  A diagonal line is parsed.
+  There's more than four changes of direction.
+  There's a discontinuity on the line (e.g., a move in the middle)
+  The line reverses direction.
+  The rectangle doesn't complete a cycle.
+  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.
+
+FIXME: Allow colinear quads and cubics to be treated like lines.
+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();)
+
+    int corners = 0;
+    SkPoint first, last;
+    first.set(0, 0);
+    last.set(0, 0);
+    int firstDirection = 0;
+    int lastDirection = 0;
+    int nextDirection = 0;
+    bool closedOrMoved = false;
+    bool autoClose = false;
+    const uint8_t* verbs = fVerbs.begin();
+    const uint8_t* verbStop = fVerbs.end();
+    const SkPoint* pts = fPts.begin();
+    while (verbs != verbStop) {
+        switch (*verbs++) {
+            case kClose_Verb:
+                pts = fPts.begin();
+                autoClose = true;
+            case kLine_Verb: {
+                SkScalar left = last.fX;
+                SkScalar top = last.fY;
+                SkScalar right = pts->fX;
+                SkScalar bottom = pts->fY;
+                ++pts;
+                if (left != right && top != bottom) {
+                    return false; // diagonal
+                }
+                if (left == right && top == bottom) {
+                    break; // single point on side OK
+                }
+                nextDirection = (left != right) << 0 |
+                    (left < right || top < bottom) << 1;
+                if (0 == corners) {
+                    firstDirection = nextDirection;
+                    first = last;
+                    last = pts[-1];
+                    corners = 1;
+                    closedOrMoved = false;
+                    break;
+                }
+                if (closedOrMoved) {
+                    return false; // closed followed by a line
+                }
+                closedOrMoved = autoClose;
+                if (lastDirection != nextDirection) {
+                    if (++corners > 4) {
+                        return false; // too many direction changes
+                    }
+                }
+                last = pts[-1];
+                if (lastDirection == nextDirection) {
+                    break; // colinear segment
+                }
+                // Possible values for corners are 2, 3, and 4.
+                // When corners == 3, nextDirection opposes firstDirection.
+                // Otherwise, nextDirection at corner 2 opposes corner 4.
+                int turn = firstDirection ^ (corners - 1);
+                int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
+                if ((directionCycle ^ turn) != nextDirection) {
+                    return false; // direction didn't follow cycle
+                }
+                break;
+            }
+            case kQuad_Verb:
+            case kCubic_Verb:
+                return false; // quadratic, cubic not allowed
+            case kMove_Verb:
+                last = *pts++;
+                closedOrMoved = true;
+                break;
+        }
+        lastDirection = nextDirection;
+    }
+    // Success if 4 corners and first point equals last
+    bool result = 4 == corners && first == last;
+    if (result && rect) {
+        *rect = getBounds();
+    }
+    return result;
+}
+
+int SkPath::getPoints(SkPoint copy[], 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;
+}
+
+SkPoint SkPath::getPoint(int index) const {
+    if ((unsigned)index < (unsigned)fPts.count()) {
+        return fPts[index];
+    }
+    return SkPoint::Make(0, 0);
+}
+
+bool SkPath::getLastPt(SkPoint* lastPt) const {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fPts.count();
+    if (count > 0) {
+        if (lastPt) {
+            *lastPt = fPts[count - 1];
+        }
+        return true;
+    }
+    if (lastPt) {
+        lastPt->set(0, 0);
+    }
+    return false;
+}
+
+void SkPath::setLastPt(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fPts.count();
+    if (count == 0) {
+        this->moveTo(x, y);
+    } else {
+        fPts[count - 1].set(x, y);
+        GEN_ID_INC;
+    }
+}
+
+void SkPath::computeBounds() const {
+    SkDEBUGCODE(this->validate();)
+    SkASSERT(fBoundsIsDirty);
+
+    fBoundsIsDirty = false;
+    compute_pt_bounds(&fBounds, fPts);
+}
+
+void SkPath::setConvexity(Convexity c) {
+    if (fConvexity != c) {
+        fConvexity = c;
+        GEN_ID_INC;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//  Construction methods
+
+#define DIRTY_AFTER_EDIT                 \
+    do {                                 \
+        fBoundsIsDirty = true;           \
+        fConvexity = kUnknown_Convexity; \
+    } while (0)
+
+#define DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE    \
+    do {                                        \
+        fBoundsIsDirty = true;                  \
+    } while (0)
+
+void SkPath::incReserve(U16CPU inc) {
+    SkDEBUGCODE(this->validate();)
+
+    fVerbs.setReserve(fVerbs.count() + inc);
+    fPts.setReserve(fPts.count() + inc);
+
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkPath::moveTo(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    int      vc = fVerbs.count();
+    SkPoint* pt;
+
+    // remember our index
+    fLastMoveToIndex = fPts.count();
+
+    pt = fPts.append();
+    *fVerbs.append() = kMove_Verb;
+    pt->set(x, y);
+
+    GEN_ID_INC;
+    DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE;
+}
+
+void SkPath::rMoveTo(SkScalar x, SkScalar y) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->moveTo(pt.fX + x, pt.fY + y);
+}
+
+void SkPath::injectMoveToIfNeeded() {
+    if (fLastMoveToIndex < 0) {
+        SkScalar x, y;
+        if (fVerbs.count() == 0) {
+            x = y = 0;
+        } else {
+            const SkPoint& pt = fPts[~fLastMoveToIndex];
+            x = pt.fX;
+            y = pt.fY;
+        }
+        this->moveTo(x, y);
+    }
+}
+
+void SkPath::lineTo(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    this->injectMoveToIfNeeded();
+
+    fPts.append()->set(x, y);
+    *fVerbs.append() = kLine_Verb;
+    fSegmentMask |= kLine_SegmentMask;
+
+    GEN_ID_INC;
+    DIRTY_AFTER_EDIT;
+}
+
+void SkPath::rLineTo(SkScalar x, SkScalar y) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->lineTo(pt.fX + x, pt.fY + y);
+}
+
+void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
+    SkDEBUGCODE(this->validate();)
+
+    this->injectMoveToIfNeeded();
+
+    SkPoint* pts = fPts.append(2);
+    pts[0].set(x1, y1);
+    pts[1].set(x2, y2);
+    *fVerbs.append() = kQuad_Verb;
+    fSegmentMask |= kQuad_SegmentMask;
+
+    GEN_ID_INC;
+    DIRTY_AFTER_EDIT;
+}
+
+void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
+}
+
+void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                     SkScalar x3, SkScalar y3) {
+    SkDEBUGCODE(this->validate();)
+
+    this->injectMoveToIfNeeded();
+
+    SkPoint* pts = fPts.append(3);
+    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;
+    DIRTY_AFTER_EDIT;
+}
+
+void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                      SkScalar x3, SkScalar y3) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
+                  pt.fX + x3, pt.fY + y3);
+}
+
+void SkPath::close() {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fVerbs.count();
+    if (count > 0) {
+        switch (fVerbs[count - 1]) {
+            case kLine_Verb:
+            case kQuad_Verb:
+            case kCubic_Verb:
+            case kMove_Verb:
+                *fVerbs.append() = kClose_Verb;
+                GEN_ID_INC;
+                break;
+            default:
+                // don't add a close if it's the first verb or a repeat
+                break;
+        }
+    }
+
+    // signal that we need a moveTo to follow us (unless we're done)
+#if 0
+    if (fLastMoveToIndex >= 0) {
+        fLastMoveToIndex = ~fLastMoveToIndex;
+    }
+#else
+    fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+    SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
+
+    this->incReserve(5);
+
+    this->moveTo(left, top);
+    if (dir == kCCW_Direction) {
+        this->lineTo(left, bottom);
+        this->lineTo(right, bottom);
+        this->lineTo(right, top);
+    } else {
+        this->lineTo(right, top);
+        this->lineTo(right, bottom);
+        this->lineTo(left, bottom);
+    }
+    this->close();
+}
+
+#define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
+
+void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
+                          Direction dir) {
+    SkScalar    w = rect.width();
+    SkScalar    halfW = SkScalarHalf(w);
+    SkScalar    h = rect.height();
+    SkScalar    halfH = SkScalarHalf(h);
+
+    if (halfW <= 0 || halfH <= 0) {
+        return;
+    }
+
+    bool skip_hori = rx >= halfW;
+    bool skip_vert = ry >= halfH;
+
+    if (skip_hori && skip_vert) {
+        this->addOval(rect, dir);
+        return;
+    }
+
+    SkAutoPathBoundsUpdate apbu(this, rect);
+
+    if (skip_hori) {
+        rx = halfW;
+    } else if (skip_vert) {
+        ry = halfH;
+    }
+
+    SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
+    SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
+
+    this->incReserve(17);
+    this->moveTo(rect.fRight - rx, rect.fTop);
+    if (dir == kCCW_Direction) {
+        if (!skip_hori) {
+            this->lineTo(rect.fLeft + rx, rect.fTop);       // top
+        }
+        this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
+                      rect.fLeft, rect.fTop + ry - sy,
+                      rect.fLeft, rect.fTop + ry);          // top-left
+        if (!skip_vert) {
+            this->lineTo(rect.fLeft, rect.fBottom - ry);        // left
+        }
+        this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
+                      rect.fLeft + rx - sx, rect.fBottom,
+                      rect.fLeft + rx, rect.fBottom);       // bot-left
+        if (!skip_hori) {
+            this->lineTo(rect.fRight - rx, rect.fBottom);   // bottom
+        }
+        this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
+                      rect.fRight, rect.fBottom - ry + sy,
+                      rect.fRight, rect.fBottom - ry);      // bot-right
+        if (!skip_vert) {
+            this->lineTo(rect.fRight, rect.fTop + ry);
+        }
+        this->cubicTo(rect.fRight, rect.fTop + ry - sy,
+                      rect.fRight - rx + sx, rect.fTop,
+                      rect.fRight - rx, rect.fTop);         // top-right
+    } else {
+        this->cubicTo(rect.fRight - rx + sx, rect.fTop,
+                      rect.fRight, rect.fTop + ry - sy,
+                      rect.fRight, rect.fTop + ry);         // top-right
+        if (!skip_vert) {
+            this->lineTo(rect.fRight, rect.fBottom - ry);
+        }
+        this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
+                      rect.fRight - rx + sx, rect.fBottom,
+                      rect.fRight - rx, rect.fBottom);      // bot-right
+        if (!skip_hori) {
+            this->lineTo(rect.fLeft + rx, rect.fBottom);    // bottom
+        }
+        this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
+                      rect.fLeft, rect.fBottom - ry + sy,
+                      rect.fLeft, rect.fBottom - ry);       // bot-left
+        if (!skip_vert) {
+            this->lineTo(rect.fLeft, rect.fTop + ry);       // left
+        }
+        this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
+                      rect.fLeft + rx - sx, rect.fTop,
+                      rect.fLeft + rx, rect.fTop);          // top-left
+        if (!skip_hori) {
+            this->lineTo(rect.fRight - rx, rect.fTop);      // top
+        }
+    }
+    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) {
+    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);
+    SkScalar    my = SkScalarMul(ry, SK_ScalarRoot2Over2);
+
+    /*
+        To handle imprecision in computing the center and radii, we revert to
+        the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
+        to ensure that we don't exceed the oval's bounds *ever*, since we want
+        to use oval for our fast-bounds, rather than have to recompute it.
+    */
+    const SkScalar L = oval.fLeft;      // cx - rx
+    const SkScalar T = oval.fTop;       // cy - ry
+    const SkScalar R = oval.fRight;     // cx + rx
+    const SkScalar B = oval.fBottom;    // cy + ry
+
+    this->incReserve(17);   // 8 quads + close
+    this->moveTo(R, cy);
+    if (dir == kCCW_Direction) {
+        this->quadTo(      R, cy - sy, cx + mx, cy - my);
+        this->quadTo(cx + sx,       T, cx     ,       T);
+        this->quadTo(cx - sx,       T, cx - mx, cy - my);
+        this->quadTo(      L, cy - sy,       L, cy     );
+        this->quadTo(      L, cy + sy, cx - mx, cy + my);
+        this->quadTo(cx - sx,       B, cx     ,       B);
+        this->quadTo(cx + sx,       B, cx + mx, cy + my);
+        this->quadTo(      R, cy + sy,       R, cy     );
+    } else {
+        this->quadTo(      R, cy + sy, cx + mx, cy + my);
+        this->quadTo(cx + sx,       B, cx     ,       B);
+        this->quadTo(cx - sx,       B, cx - mx, cy + my);
+        this->quadTo(      L, cy + sy,       L, cy     );
+        this->quadTo(      L, cy - sy, cx - mx, cy - my);
+        this->quadTo(cx - sx,       T, cx     ,       T);
+        this->quadTo(cx + sx,       T, cx + mx, cy - my);
+        this->quadTo(      R, cy - sy,       R, cy     );
+    }
+#endif
+    this->close();
+}
+
+void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
+    if (r > 0) {
+        SkRect  rect;
+        rect.set(x - r, y - r, x + r, y + r);
+        this->addOval(rect, dir);
+    }
+}
+
+#include "SkGeometry.h"
+
+static int build_arc_points(const SkRect& oval, SkScalar startAngle,
+                            SkScalar sweepAngle,
+                            SkPoint pts[kSkBuildQuadArcStorage]) {
+    SkVector start, stop;
+
+    start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
+    stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
+                             &stop.fX);
+
+    /*  If the sweep angle is nearly (but less than) 360, then due to precision
+        loss in radians-conversion and/or sin/cos, we may end up with coincident
+        vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
+        of drawing a nearly complete circle (good).
+             e.g. canvas.drawArc(0, 359.99, ...)
+             -vs- canvas.drawArc(0, 359.9, ...)
+        We try to detect this edge case, and tweak the stop vector
+     */
+    if (start == stop) {
+        SkScalar sw = SkScalarAbs(sweepAngle);
+        if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
+            SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
+            // make a guess at a tiny angle (in radians) to tweak by
+            SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
+            // not sure how much will be enough, so we use a loop
+            do {
+                stopRad -= deltaRad;
+                stop.fY = SkScalarSinCos(stopRad, &stop.fX);
+            } while (start == stop);
+        }
+    }
+
+    SkMatrix    matrix;
+
+    matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
+    matrix.postTranslate(oval.centerX(), oval.centerY());
+
+    return SkBuildQuadArc(start, stop,
+          sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
+                          &matrix, pts);
+}
+
+void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+                   bool forceMoveTo) {
+    if (oval.width() < 0 || oval.height() < 0) {
+        return;
+    }
+
+    SkPoint pts[kSkBuildQuadArcStorage];
+    int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+    SkASSERT((count & 1) == 1);
+
+    if (fVerbs.count() == 0) {
+        forceMoveTo = true;
+    }
+    this->incReserve(count);
+    forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
+    for (int i = 1; i < count; i += 2) {
+        this->quadTo(pts[i], pts[i+1]);
+    }
+}
+
+void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
+                    SkScalar sweepAngle) {
+    if (oval.isEmpty() || 0 == sweepAngle) {
+        return;
+    }
+
+    const SkScalar kFullCircleAngle = SkIntToScalar(360);
+
+    if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
+        this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
+        return;
+    }
+
+    SkPoint pts[kSkBuildQuadArcStorage];
+    int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+
+    this->incReserve(count);
+    this->moveTo(pts[0]);
+    for (int i = 1; i < count; i += 2) {
+        this->quadTo(pts[i], pts[i+1]);
+    }
+}
+
+/*
+    Need to handle the case when the angle is sharp, and our computed end-points
+    for the arc go behind pt1 and/or p2...
+*/
+void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                   SkScalar radius) {
+    SkVector    before, after;
+
+    // need to know our prev pt so we can construct tangent vectors
+    {
+        SkPoint start;
+        this->getLastPt(&start);
+        // Handle degenerate cases by adding a line to the first point and
+        // bailing out.
+        if ((x1 == start.fX && y1 == start.fY) ||
+            (x1 == x2 && y1 == y2) ||
+            radius == 0) {
+            this->lineTo(x1, y1);
+            return;
+        }
+        before.setNormalize(x1 - start.fX, y1 - start.fY);
+        after.setNormalize(x2 - x1, y2 - y1);
+    }
+
+    SkScalar cosh = SkPoint::DotProduct(before, after);
+    SkScalar sinh = SkPoint::CrossProduct(before, after);
+
+    if (SkScalarNearlyZero(sinh)) {   // angle is too tight
+        this->lineTo(x1, y1);
+        return;
+    }
+
+    SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
+    if (dist < 0) {
+        dist = -dist;
+    }
+
+    SkScalar xx = x1 - SkScalarMul(dist, before.fX);
+    SkScalar yy = y1 - SkScalarMul(dist, before.fY);
+    SkRotationDirection arcDir;
+
+    // now turn before/after into normals
+    if (sinh > 0) {
+        before.rotateCCW();
+        after.rotateCCW();
+        arcDir = kCW_SkRotationDirection;
+    } else {
+        before.rotateCW();
+        after.rotateCW();
+        arcDir = kCCW_SkRotationDirection;
+    }
+
+    SkMatrix    matrix;
+    SkPoint     pts[kSkBuildQuadArcStorage];
+
+    matrix.setScale(radius, radius);
+    matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
+                         yy - SkScalarMul(radius, before.fY));
+
+    int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
+
+    this->incReserve(count);
+    // [xx,yy] == pts[0]
+    this->lineTo(xx, yy);
+    for (int i = 1; i < count; i += 2) {
+        this->quadTo(pts[i], pts[i+1]);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
+    SkMatrix matrix;
+
+    matrix.setTranslate(dx, dy);
+    this->addPath(path, matrix);
+}
+
+void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
+    this->incReserve(path.fPts.count());
+
+    RawIter iter(path);
+    SkPoint pts[4];
+    Verb    verb;
+
+    SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
+
+    while ((verb = iter.next(pts)) != kDone_Verb) {
+        switch (verb) {
+            case kMove_Verb:
+                proc(matrix, &pts[0], &pts[0], 1);
+                this->moveTo(pts[0]);
+                break;
+            case kLine_Verb:
+                proc(matrix, &pts[1], &pts[1], 1);
+                this->lineTo(pts[1]);
+                break;
+            case kQuad_Verb:
+                proc(matrix, &pts[1], &pts[1], 2);
+                this->quadTo(pts[1], pts[2]);
+                break;
+            case kCubic_Verb:
+                proc(matrix, &pts[1], &pts[1], 3);
+                this->cubicTo(pts[1], pts[2], pts[3]);
+                break;
+            case kClose_Verb:
+                this->close();
+                break;
+            default:
+                SkDEBUGFAIL("unknown verb");
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t gPtsInVerb[] = {
+    1,  // kMove
+    1,  // kLine
+    2,  // kQuad
+    3,  // kCubic
+    0,  // kClose
+    0   // kDone
+};
+
+// 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) {
+        return;
+    }
+
+    this->incReserve(vcount);
+
+    const uint8_t*  verbs = path.fVerbs.begin();
+    const SkPoint*  pts = path.fPts.begin() + 1;    // 1 for the initial moveTo
+
+    SkASSERT(verbs[0] == kMove_Verb);
+    for (i = 1; i < vcount; i++) {
+        switch (verbs[i]) {
+            case kLine_Verb:
+                this->lineTo(pts[0].fX, pts[0].fY);
+                break;
+            case kQuad_Verb:
+                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);
+                break;
+            case kClose_Verb:
+                return;
+        }
+        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) {
+        return;
+    }
+
+    this->incReserve(vcount);
+
+    const uint8_t*  verbs = path.fVerbs.begin();
+    const SkPoint*  pts = path.fPts.begin();
+
+    SkASSERT(verbs[0] == kMove_Verb);
+    for (i = 1; i < vcount; i++) {
+        int n = gPtsInVerb[verbs[i]];
+        if (n == 0) {
+            break;
+        }
+        pts += n;
+    }
+
+    while (--i > 0) {
+        switch (verbs[i]) {
+            case kLine_Verb:
+                this->lineTo(pts[-1].fX, pts[-1].fY);
+                break;
+            case kQuad_Verb:
+                this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
+                break;
+            case kCubic_Verb:
+                this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
+                              pts[-3].fX, pts[-3].fY);
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                break;
+        }
+        pts -= gPtsInVerb[verbs[i]];
+    }
+}
+
+void SkPath::reverseAddPath(const SkPath& src) {
+    this->incReserve(src.fPts.count());
+
+    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();
+
+    bool needMove = true;
+    bool needClose = false;
+    while (verbs > startVerbs) {
+        uint8_t v = *--verbs;
+        int n = gPtsInVerb[v];
+
+        if (needMove) {
+            --pts;
+            this->moveTo(pts->fX, pts->fY);
+            needMove = false;
+        }
+        pts -= n;
+        switch (v) {
+            case kMove_Verb:
+                if (needClose) {
+                    this->close();
+                    needClose = false;
+                }
+                needMove = true;
+                pts += 1;   // so we see the point in "if (needMove)" above
+                break;
+            case kLine_Verb:
+                this->lineTo(pts[0]);
+                break;
+            case kQuad_Verb:
+                this->quadTo(pts[1], pts[0]);
+                break;
+            case kCubic_Verb:
+                this->cubicTo(pts[2], pts[1], pts[0]);
+                break;
+            case kClose_Verb:
+                needClose = true;
+                break;
+            default:
+                SkASSERT(!"unexpected verb");
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
+    SkMatrix    matrix;
+
+    matrix.setTranslate(dx, dy);
+    this->transform(matrix, dst);
+}
+
+#include "SkGeometry.h"
+
+static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
+                              int level = 2) {
+    if (--level >= 0) {
+        SkPoint tmp[5];
+
+        SkChopQuadAtHalf(pts, tmp);
+        subdivide_quad_to(path, &tmp[0], level);
+        subdivide_quad_to(path, &tmp[2], level);
+    } else {
+        path->quadTo(pts[1], pts[2]);
+    }
+}
+
+static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
+                               int level = 2) {
+    if (--level >= 0) {
+        SkPoint tmp[7];
+
+        SkChopCubicAtHalf(pts, tmp);
+        subdivide_cubic_to(path, &tmp[0], level);
+        subdivide_cubic_to(path, &tmp[3], level);
+    } else {
+        path->cubicTo(pts[1], pts[2], pts[3]);
+    }
+}
+
+void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
+    SkDEBUGCODE(this->validate();)
+    if (dst == NULL) {
+        dst = (SkPath*)this;
+    }
+
+    if (matrix.hasPerspective()) {
+        SkPath  tmp;
+        tmp.fFillType = fFillType;
+
+        SkPath::Iter    iter(*this, false);
+        SkPoint         pts[4];
+        SkPath::Verb    verb;
+
+        while ((verb = iter.next(pts)) != kDone_Verb) {
+            switch (verb) {
+                case kMove_Verb:
+                    tmp.moveTo(pts[0]);
+                    break;
+                case kLine_Verb:
+                    tmp.lineTo(pts[1]);
+                    break;
+                case kQuad_Verb:
+                    subdivide_quad_to(&tmp, pts);
+                    break;
+                case kCubic_Verb:
+                    subdivide_cubic_to(&tmp, pts);
+                    break;
+                case kClose_Verb:
+                    tmp.close();
+                    break;
+                default:
+                    SkDEBUGFAIL("unknown verb");
+                    break;
+            }
+        }
+
+        // swap() will increment the gen id if needed
+        dst->swap(tmp);
+        matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
+    } 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);
+            dst->fBoundsIsDirty = false;
+        } else {
+            dst->fBoundsIsDirty = true;
+        }
+
+        if (this != dst) {
+            dst->fVerbs = fVerbs;
+            dst->fPts.setCount(fPts.count());
+            dst->fFillType = fFillType;
+            dst->fSegmentMask = fSegmentMask;
+            dst->fConvexity = fConvexity;
+        }
+
+        if (!matrix.isIdentity()) {
+            GEN_ID_PTR_INC(dst);
+        }
+        matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
+
+        SkDEBUGCODE(dst->validate();)
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enum SegmentState {
+    kEmptyContour_SegmentState,   // The current contour is empty. We may be
+                                  // starting processing or we may have just
+                                  // closed a contour.
+    kAfterMove_SegmentState,      // We have seen a move, but nothing else.
+    kAfterPrimitive_SegmentState  // We have seen a primitive but not yet
+                                  // closed the path. Also the initial state.
+};
+
+SkPath::Iter::Iter() {
+#ifdef SK_DEBUG
+    fPts = NULL;
+    fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
+    fForceClose = fCloseLine = false;
+    fSegmentState = kEmptyContour_SegmentState;
+#endif
+    // need to init enough to make next() harmlessly return kDone_Verb
+    fVerbs = NULL;
+    fVerbStop = NULL;
+    fNeedClose = false;
+}
+
+SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
+    this->setPath(path, forceClose);
+}
+
+void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
+    fPts = path.fPts.begin();
+    fVerbs = path.fVerbs.begin();
+    fVerbStop = path.fVerbs.end();
+    fLastPt.fX = fLastPt.fY = 0;
+    fMoveTo.fX = fMoveTo.fY = 0;
+    fForceClose = SkToU8(forceClose);
+    fNeedClose = false;
+    fSegmentState = kEmptyContour_SegmentState;
+}
+
+bool SkPath::Iter::isClosedContour() const {
+    if (fVerbs == NULL || fVerbs == fVerbStop) {
+        return false;
+    }
+    if (fForceClose) {
+        return true;
+    }
+
+    const uint8_t* verbs = fVerbs;
+    const uint8_t* stop = fVerbStop;
+
+    if (kMove_Verb == *verbs) {
+        verbs += 1; // skip the initial moveto
+    }
+
+    while (verbs < stop) {
+        unsigned v = *verbs++;
+        if (kMove_Verb == v) {
+            break;
+        }
+        if (kClose_Verb == v) {
+            return true;
+        }
+    }
+    return false;
+}
+
+SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
+    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.
+        // (consider SkPoint is a 2-dimension float point).
+        if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
+            SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
+            return kClose_Verb;
+        }
+
+        if (pts) {
+            pts[0] = fLastPt;
+            pts[1] = fMoveTo;
+        }
+        fLastPt = fMoveTo;
+        fCloseLine = true;
+        return kLine_Verb;
+    } else {
+        pts[0] = fMoveTo;
+        return kClose_Verb;
+    }
+}
+
+bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
+    if (fSegmentState == kAfterMove_SegmentState) {
+        // Set the first return pt to the move pt
+        if (pts) {
+            *pts = fMoveTo;
+        }
+        fSegmentState = kAfterPrimitive_SegmentState;
+    } else {
+        SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
+         // Set the first return pt to the last pt of the previous primitive.
+        if (pts) {
+            *pts = fPts[-1];
+        }
+    }
+    return false;
+}
+
+void SkPath::Iter::consumeDegenerateSegments() {
+    // We need to step over anything that will not move the current draw point
+    // forward before the next move is seen
+    const uint8_t* lastMoveVerb = 0;
+    const SkPoint* lastMovePt = 0;
+    SkPoint lastPt = fLastPt;
+    while (fVerbs != fVerbStop) {
+        unsigned verb = *fVerbs;
+        switch (verb) {
+            case kMove_Verb:
+                // Keep a record of this most recent move
+                lastMoveVerb = fVerbs;
+                lastMovePt = fPts;
+                lastPt = fPts[0];
+                fVerbs++;
+                fPts++;
+                break;
+
+            case kClose_Verb:
+                // A close when we are in a segment is always valid
+                if (fSegmentState == kAfterPrimitive_SegmentState) {
+                    return;
+                }
+                // A close at any other time must be ignored
+                fVerbs++;
+                break;
+
+            case kLine_Verb:
+                if (!IsLineDegenerate(lastPt, fPts[0])) {
+                    if (lastMoveVerb) {
+                        fVerbs = lastMoveVerb;
+                        fPts = lastMovePt;
+                        return;
+                    }
+                    return;
+                }
+                // Ignore this line and continue
+                fVerbs++;
+                fPts++;
+                break;
+
+            case kQuad_Verb:
+                if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1])) {
+                    if (lastMoveVerb) {
+                        fVerbs = lastMoveVerb;
+                        fPts = lastMovePt;
+                        return;
+                    }
+                    return;
+                }
+                // Ignore this line and continue
+                fVerbs++;
+                fPts += 2;
+                break;
+
+            case kCubic_Verb:
+                if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2])) {
+                    if (lastMoveVerb) {
+                        fVerbs = lastMoveVerb;
+                        fPts = lastMovePt;
+                        return;
+                    }
+                    return;
+                }
+                // Ignore this line and continue
+                fVerbs++;
+                fPts += 3;
+                break;
+
+            default:
+                SkDEBUGFAIL("Should never see kDone_Verb");
+        }
+    }
+}
+
+SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
+    this->consumeDegenerateSegments();
+
+    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)) {
+                return kLine_Verb;
+            }
+            fNeedClose = false;
+            return kClose_Verb;
+        }
+        return kDone_Verb;
+    }
+
+    unsigned        verb = *fVerbs++;
+    const SkPoint*  srcPts = fPts;
+
+    switch (verb) {
+        case kMove_Verb:
+            if (fNeedClose) {
+                fVerbs -= 1;
+                verb = this->autoClose(pts);
+                if (verb == kClose_Verb) {
+                    fNeedClose = false;
+                }
+                return (Verb)verb;
+            }
+            if (fVerbs == fVerbStop) {    // might be a trailing moveto
+                return kDone_Verb;
+            }
+            fMoveTo = *srcPts;
+            if (pts) {
+                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];
+            }
+            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));
+            }
+            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));
+            }
+            fLastPt = srcPts[2];
+            srcPts += 3;
+            break;
+        case kClose_Verb:
+            verb = this->autoClose(pts);
+            if (verb == kLine_Verb) {
+                fVerbs -= 1;
+            } else {
+                fNeedClose = false;
+                fSegmentState = kEmptyContour_SegmentState;
+            }
+            fLastPt = fMoveTo;
+            break;
+    }
+    fPts = srcPts;
+    return (Verb)verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPath::RawIter::RawIter() {
+#ifdef SK_DEBUG
+    fPts = NULL;
+    fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
+#endif
+    // need to init enough to make next() harmlessly return kDone_Verb
+    fVerbs = NULL;
+    fVerbStop = NULL;
+}
+
+SkPath::RawIter::RawIter(const SkPath& path) {
+    this->setPath(path);
+}
+
+void SkPath::RawIter::setPath(const SkPath& path) {
+    fPts = path.fPts.begin();
+    fVerbs = path.fVerbs.begin();
+    fVerbStop = path.fVerbs.end();
+    fMoveTo.fX = fMoveTo.fY = 0;
+    fLastPt.fX = fLastPt.fY = 0;
+}
+
+SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
+    if (fVerbs == fVerbStop) {
+        return kDone_Verb;
+    }
+
+    unsigned        verb = *fVerbs++;
+    const SkPoint*  srcPts = fPts;
+
+    switch (verb) {
+        case kMove_Verb:
+            if (pts) {
+                pts[0] = *srcPts;
+            }
+            fMoveTo = srcPts[0];
+            fLastPt = fMoveTo;
+            srcPts += 1;
+            break;
+        case kLine_Verb:
+            if (pts) {
+                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));
+            }
+            fLastPt = srcPts[1];
+            srcPts += 2;
+            break;
+        case kCubic_Verb:
+            if (pts) {
+                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;
+            }
+            break;
+    }
+    fPts = srcPts;
+    return (Verb)verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+    Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
+*/
+
+void SkPath::flatten(SkWriter32& buffer) 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());
+}
+
+void SkPath::unflatten(SkReader32& buffer) {
+    fPts.setCount(buffer.readS32());
+    fVerbs.setCount(buffer.readS32());
+    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());
+
+    GEN_ID_INC;
+    DIRTY_AFTER_EDIT;
+
+    SkDEBUGCODE(this->validate();)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::dump(bool forceClose, const char title[]) const {
+    Iter    iter(*this, forceClose);
+    SkPoint pts[4];
+    Verb    verb;
+
+    SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
+             title ? title : "");
+
+    while ((verb = iter.next(pts)) != 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
+                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
+                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
+                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
+                break;
+            case kClose_Verb:
+                SkDebugf("  path: close\n");
+                break;
+            default:
+                SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
+                verb = kDone_Verb;  // stop the loop
+                break;
+        }
+    }
+    SkDebugf("path: done %s\n", title ? title : "");
+}
+
+void SkPath::dump() const {
+    this->dump(false);
+}
+
+#ifdef SK_DEBUG
+void SkPath::validate() const {
+    SkASSERT(this != NULL);
+    SkASSERT((fFillType & ~3) == 0);
+    fPts.validate();
+    fVerbs.validate();
+
+    if (!fBoundsIsDirty) {
+        SkRect bounds;
+        compute_pt_bounds(&bounds, fPts);
+        if (fPts.count() <= 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
+            // be [2, 2, 2, 2]
+            SkASSERT(bounds.isEmpty());
+            SkASSERT(fBounds.isEmpty());
+        } else {
+            if (bounds.isEmpty()) {
+                SkASSERT(fBounds.isEmpty());
+            } else {
+                if (!fBounds.isEmpty()) {
+                    SkASSERT(fBounds.contains(bounds));
+                }
+            }
+        }
+    }
+
+    uint32_t mask = 0;
+    for (int i = 0; i < fVerbs.count(); i++) {
+        switch (fVerbs[i]) {
+            case kLine_Verb:
+                mask |= kLine_SegmentMask;
+                break;
+            case kQuad_Verb:
+                mask |= kQuad_SegmentMask;
+                break;
+            case kCubic_Verb:
+                mask |= kCubic_SegmentMask;
+        }
+    }
+    SkASSERT(mask == fSegmentMask);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int sign(SkScalar x) { return x < 0; }
+#define kValueNeverReturnedBySign   2
+
+static int CrossProductSign(const SkVector& a, const SkVector& b) {
+    return SkScalarSignAsInt(SkPoint::CrossProduct(a, b));
+}
+
+// only valid for a single contour
+struct Convexicator {
+    Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) {
+        fSign = 0;
+        // warnings
+        fCurrPt.set(0, 0);
+        fVec0.set(0, 0);
+        fVec1.set(0, 0);
+        fFirstVec.set(0, 0);
+
+        fDx = fDy = 0;
+        fSx = fSy = kValueNeverReturnedBySign;
+    }
+
+    SkPath::Convexity getConvexity() const { return fConvexity; }
+
+    void addPt(const SkPoint& pt) {
+        if (SkPath::kConcave_Convexity == fConvexity) {
+            return;
+        }
+
+        if (0 == fPtCount) {
+            fCurrPt = pt;
+            ++fPtCount;
+        } else {
+            SkVector vec = pt - fCurrPt;
+            if (vec.fX || vec.fY) {
+                fCurrPt = pt;
+                if (++fPtCount == 2) {
+                    fFirstVec = fVec1 = vec;
+                } else {
+                    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;
+                }
+            }
+        }
+    }
+
+    void close() {
+        if (fPtCount > 2) {
+            this->addVec(fFirstVec);
+        }
+    }
+
+private:
+    void addVec(const SkVector& vec) {
+        SkASSERT(vec.fX || vec.fY);
+        fVec0 = fVec1;
+        fVec1 = vec;
+        int sign = CrossProductSign(fVec0, fVec1);
+        if (0 == fSign) {
+            fSign = sign;
+        } else if (sign) {
+            if (fSign != sign) {
+                fConvexity = SkPath::kConcave_Convexity;
+            }
+        }
+    }
+
+    SkPoint             fCurrPt;
+    SkVector            fVec0, fVec1, fFirstVec;
+    int                 fPtCount;   // non-degenerate points
+    int                 fSign;
+    SkPath::Convexity   fConvexity;
+    int                 fDx, fDy, fSx, fSy;
+};
+
+SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) {
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+    SkPath::Iter    iter(path, true);
+
+    int             contourCount = 0;
+    int             count;
+    Convexicator    state;
+
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case kMove_Verb:
+                if (++contourCount > 1) {
+                    return kConcave_Convexity;
+                }
+                pts[1] = pts[0];
+                count = 1;
+                break;
+            case kLine_Verb: count = 1; break;
+            case kQuad_Verb: count = 2; break;
+            case kCubic_Verb: count = 3; break;
+            case kClose_Verb:
+                state.close();
+                count = 0;
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return kConcave_Convexity;
+        }
+
+        for (int i = 1; i <= count; i++) {
+            state.addPt(pts[i]);
+        }
+        // early exit
+        if (kConcave_Convexity == state.getConvexity()) {
+            return kConcave_Convexity;
+        }
+    }
+    return state.getConvexity();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ContourIter {
+public:
+    ContourIter(const SkTDArray<uint8_t>& verbs, const SkTDArray<SkPoint>& pts);
+
+    bool done() const { return fDone; }
+    // if !done() then these may be called
+    int count() const { return fCurrPtCount; }
+    const SkPoint* pts() const { return fCurrPt; }
+    void next();
+
+private:
+    int fCurrPtCount;
+    const SkPoint* fCurrPt;
+    const uint8_t* fCurrVerb;
+    const uint8_t* fStopVerbs;
+    bool fDone;
+    SkDEBUGCODE(int fContourCounter;)
+};
+
+ContourIter::ContourIter(const SkTDArray<uint8_t>& verbs,
+                         const SkTDArray<SkPoint>& pts) {
+    fStopVerbs = verbs.begin() + verbs.count();
+    
+    fDone = false;
+    fCurrPt = pts.begin();
+    fCurrVerb = verbs.begin();
+    fCurrPtCount = 0;
+    SkDEBUGCODE(fContourCounter = 0;)
+    this->next();
+}
+
+void ContourIter::next() {
+    if (fCurrVerb >= fStopVerbs) {
+        fDone = true;
+    }
+    if (fDone) {
+        return;
+    }
+
+    // skip pts of prev contour
+    fCurrPt += fCurrPtCount;
+
+    SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]);
+    int ptCount = 1;    // moveTo
+    const uint8_t* verbs = fCurrVerb;
+
+    for (++verbs; verbs < fStopVerbs; ++verbs) {
+        switch (*verbs) {
+            case SkPath::kMove_Verb:
+                goto CONTOUR_END;
+            case SkPath::kLine_Verb:
+                ptCount += 1;
+                break;
+            case SkPath::kQuad_Verb:
+                ptCount += 2;
+                break;
+            case SkPath::kCubic_Verb:
+                ptCount += 3;
+                break;
+            default:    // kClose_Verb, just keep going
+                break;
+        }
+    }
+CONTOUR_END:
+    fCurrPtCount = ptCount;
+    fCurrVerb = verbs;
+    SkDEBUGCODE(++fContourCounter;)
+}
+
+// returns cross product of (p1 - p0) and (p2 - p0)
+static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
+    SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
+    // We may get 0 when the above subtracts underflow. We expect this to be
+    // very rare and lazily promote to double.
+    if (0 == cross) {
+        double p0x = SkScalarToDouble(p0.fX);
+        double p0y = SkScalarToDouble(p0.fY);
+
+        double p1x = SkScalarToDouble(p1.fX);
+        double p1y = SkScalarToDouble(p1.fY);
+
+        double p2x = SkScalarToDouble(p2.fX);
+        double p2y = SkScalarToDouble(p2.fY);
+
+        cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
+                                 (p1y - p0y) * (p2x - p0x));
+
+    }
+    return cross;
+}
+
+// Returns the first pt with the maximum Y coordinate
+static int find_max_y(const SkPoint pts[], int count) {
+    SkASSERT(count > 0);
+    SkScalar max = pts[0].fY;
+    int firstIndex = 0;
+    for (int i = 1; i < count; ++i) {
+        SkScalar y = pts[i].fY;
+        if (y > max) {
+            max = y;
+            firstIndex = i;
+        }
+    }
+    return firstIndex;
+}
+
+static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
+    int i = index;
+    for (;;) {
+        i = (i + inc) % n;
+        if (i == index) {   // we wrapped around, so abort
+            break;
+        }
+        if (pts[index] != pts[i]) { // found a different point, success!
+            break;
+        }
+    }
+    return i;
+}
+
+/**
+ *  Starting at index, and moving forward (incrementing), find the xmin and
+ *  xmax of the contiguous points that have the same Y.
+ */
+static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
+                               int* maxIndexPtr) {
+    const SkScalar y = pts[index].fY;
+    SkScalar min = pts[index].fX;
+    SkScalar max = min;
+    int minIndex = index;
+    int maxIndex = index;
+    for (int i = index + 1; i < n; ++i) {
+        if (pts[i].fY != y) {
+            break;
+        }
+        SkScalar x = pts[i].fX;
+        if (x < min) {
+            min = x;
+            minIndex = i;
+        } else if (x > max) {
+            max = x;
+            maxIndex = i;
+        }
+    }
+    *maxIndexPtr = maxIndex;
+    return minIndex;
+}
+
+static bool crossToDir(SkScalar cross, SkPath::Direction* dir) {
+    if (dir) {
+        *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+    }
+    return true;
+}
+
+#if 0
+#include "SkString.h"
+#include "../utils/SkParsePath.h"
+static void dumpPath(const SkPath& path) {
+    SkString str;
+    SkParsePath::ToSVGString(path, &str);
+    SkDebugf("%s\n", str.c_str());
+}
+#endif
+
+/*
+ *  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
+ *  contour, we may find one that is wound the opposite way (correctly) since
+ *  it is the interior of a hole (e.g. 'o'). Thus we must find the contour
+ *  that is outer most (or at least has the global y-max) before we can consider
+ *  its cross product.
+ */
+bool SkPath::cheapComputeDirection(Direction* dir) const {
+//    dumpPath(*this);
+    // don't want to pay the cost for computing this if it
+    // is unknown, so we don't call isConvex()
+    const Convexity conv = this->getConvexityOrUnknown();
+
+    ContourIter iter(fVerbs, fPts);
+
+    // initialize with our logical y-min
+    SkScalar ymax = this->getBounds().fTop;
+    SkScalar ymaxCross = 0;
+
+    for (; !iter.done(); iter.next()) {
+        int n = iter.count();
+        if (n < 3) {
+            continue;
+        }
+
+        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);
+                }
+            }
+        } else {
+            int index = find_max_y(pts, n);
+            if (pts[index].fY < ymax) {
+                continue;
+            }
+
+            // If there is more than 1 distinct point at the y-max, we take the
+            // x-min and x-max of them and just subtract to compute the dir.
+            if (pts[(index + 1) % n].fY == pts[index].fY) {
+                int maxIndex;
+                int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
+                if (minIndex == maxIndex) {
+                    goto TRY_CROSSPROD;
+                }
+                SkASSERT(pts[minIndex].fY == pts[index].fY);
+                SkASSERT(pts[maxIndex].fY == pts[index].fY);
+                SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
+                // we just subtract the indices, and let that auto-convert to
+                // SkScalar, since we just want - or + to signal the direction.
+                cross = minIndex - maxIndex;
+            } else {
+                TRY_CROSSPROD:
+                // Find a next and prev index to use for the cross-product test,
+                // but we try to find pts that form non-zero vectors from pts[index]
+                //
+                // 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);
+                if (prev == index) {
+                    // completely degenerate, skip to next contour
+                    continue;
+                }
+                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) {
+                    // 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;
+                ymaxCross = cross;
+            }
+        }
+    }
+
+    return ymaxCross ? crossToDir(ymaxCross, dir) : false;
+}
diff --git a/legacy/src/core/SkPathEffect.cpp b/legacy/src/core/SkPathEffect.cpp
new file mode 100644
index 0000000..d81ac9f
--- /dev/null
+++ b/legacy/src/core/SkPathEffect.cpp
@@ -0,0 +1,142 @@
+
+/*
+ * 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 "SkPathEffect.h"
+#include "SkPath.h"
+#include "SkBuffer.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPairPathEffect::SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1)
+        : fPE0(pe0), fPE1(pe1) {
+    SkASSERT(pe0);
+    SkASSERT(pe1);
+    fPE0->ref();
+    fPE1->ref();
+}
+
+SkPairPathEffect::~SkPairPathEffect() {
+    SkSafeUnref(fPE0);
+    SkSafeUnref(fPE1);
+}
+
+/*
+    Format: [oe0-factory][pe1-factory][pe0-size][pe0-data][pe1-data]
+*/
+void SkPairPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+    buffer.writeFlattenable(fPE0);
+    buffer.writeFlattenable(fPE1);
+}
+
+SkPairPathEffect::SkPairPathEffect(SkFlattenableReadBuffer& buffer) {
+    fPE0 = (SkPathEffect*)buffer.readFlattenable();
+    fPE1 = (SkPathEffect*)buffer.readFlattenable();
+    // either of these may fail, so we have to check for nulls later on
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkComposePathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                     SkScalar* width) {
+    // we may have failed to unflatten these, so we have to check
+    if (!fPE0 || !fPE1) {
+        return false;
+    }
+
+    SkPath          tmp;
+    const SkPath*   ptr = &src;
+
+    if (fPE1->filterPath(&tmp, src, width)) {
+        ptr = &tmp;
+    }
+    return fPE0->filterPath(dst, *ptr, width);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                 SkScalar* width) {
+    // 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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/legacy/src/core/SkPathHeap.cpp b/legacy/src/core/SkPathHeap.cpp
new file mode 100644
index 0000000..9bf77c1
--- /dev/null
+++ b/legacy/src/core/SkPathHeap.cpp
@@ -0,0 +1,62 @@
+
+/*
+ * 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 "SkPathHeap.h"
+#include "SkPath.h"
+#include "SkStream.h"
+#include "SkFlattenable.h"
+#include <new>
+
+#define kPathCount  64
+
+SkPathHeap::SkPathHeap() : fHeap(kPathCount * sizeof(SkPath)) {
+}
+
+SkPathHeap::SkPathHeap(SkFlattenableReadBuffer& buffer)
+            : fHeap(kPathCount * sizeof(SkPath)) {
+    int count = buffer.readS32();
+
+    fPaths.setCount(count);
+    SkPath** ptr = fPaths.begin();
+    SkPath* p = (SkPath*)fHeap.allocThrow(count * sizeof(SkPath));
+
+    for (int i = 0; i < count; i++) {
+        new (p) SkPath;
+        p->unflatten(buffer);
+        *ptr++ = p; // record the pointer
+        p++;        // move to the next storage location
+    }
+}
+
+SkPathHeap::~SkPathHeap() {
+    SkPath** iter = fPaths.begin();
+    SkPath** stop = fPaths.end();
+    while (iter < stop) {
+        (*iter)->~SkPath();
+        iter++;
+    }
+}
+
+int SkPathHeap::append(const SkPath& path) {
+    SkPath* p = (SkPath*)fHeap.allocThrow(sizeof(SkPath));
+    new (p) SkPath(path);
+    *fPaths.append() = p;
+    return fPaths.count();
+}
+
+void SkPathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
+    int count = fPaths.count();
+    
+    buffer.write32(count);
+    SkPath** iter = fPaths.begin();
+    SkPath** stop = fPaths.end();
+    while (iter < stop) {
+        (*iter)->flatten(buffer);
+        iter++;
+    }
+}
+
diff --git a/legacy/src/core/SkPathHeap.h b/legacy/src/core/SkPathHeap.h
new file mode 100644
index 0000000..99c2626
--- /dev/null
+++ b/legacy/src/core/SkPathHeap.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 SkPathHeap_DEFINED
+#define SkPathHeap_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkChunkAlloc.h"
+#include "SkTDArray.h"
+
+class SkPath;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+class SkPathHeap : public SkRefCnt {
+public:
+            SkPathHeap();
+            SkPathHeap(SkFlattenableReadBuffer&);
+    virtual ~SkPathHeap();
+
+    /** Copy the path into the heap, and return the new total number of paths.
+        Thus, the returned value will be index+1, where index is the index of
+        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;
+};
+
+#endif
+
diff --git a/legacy/src/core/SkPathMeasure.cpp b/legacy/src/core/SkPathMeasure.cpp
new file mode 100644
index 0000000..04d19c2
--- /dev/null
+++ b/legacy/src/core/SkPathMeasure.cpp
@@ -0,0 +1,535 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkPathMeasure.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+#include "SkTSearch.h"
+
+// these must be 0,1,2 since they are in our 2-bit field
+enum {
+    kLine_SegType,
+    kQuad_SegType,
+    kCubic_SegType
+};
+
+#define kMaxTValue  32767
+
+static inline SkScalar tValue2Scalar(int t) {
+    SkASSERT((unsigned)t <= kMaxTValue);
+
+#ifdef SK_SCALAR_IS_FLOAT
+    return t * 3.05185e-5f; // t / 32767
+#else
+    return (t + (t >> 14)) << 1;
+#endif
+}
+
+SkScalar SkPathMeasure::Segment::getScalarT() const {
+    return tValue2Scalar(fTValue);
+}
+
+const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) {
+    unsigned ptIndex = seg->fPtIndex;
+
+    do {
+        ++seg;
+    } while (seg->fPtIndex == ptIndex);
+    return seg;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline int tspan_big_enough(int tspan) {
+    SkASSERT((unsigned)tspan <= kMaxTValue);
+    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
+#define CHEAP_DIST_LIMIT    (SK_Scalar1/2)  // just made this value up
+
+static bool quad_too_curvy(const SkPoint pts[3]) {
+    // diff = (a/4 + b/2 + c/4) - (a/2 + c/2)
+    // diff = -a/4 + b/2 - c/4
+    SkScalar dx = SkScalarHalf(pts[1].fX) -
+                        SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX));
+    SkScalar dy = SkScalarHalf(pts[1].fY) -
+                        SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY));
+
+    SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy));
+    return dist > CHEAP_DIST_LIMIT;
+}
+
+static bool cheap_dist_exceeds_limit(const SkPoint& pt,
+                                     SkScalar x, SkScalar y) {
+    SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY));
+    // just made up the 1/2
+    return dist > CHEAP_DIST_LIMIT;
+}
+
+static bool cubic_too_curvy(const SkPoint pts[4]) {
+    return  cheap_dist_exceeds_limit(pts[1],
+                         SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3),
+                         SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3))
+                         ||
+            cheap_dist_exceeds_limit(pts[2],
+                         SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3),
+                         SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3));
+}
+
+SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3],
+                          SkScalar distance, int mint, int maxt, int ptIndex) {
+    if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) {
+        SkPoint tmp[5];
+        int     halft = (mint + maxt) >> 1;
+
+        SkChopQuadAtHalf(pts, tmp);
+        distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex);
+        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;
+            Segment* seg = fSegments.append();
+            seg->fDistance = distance;
+            seg->fPtIndex = ptIndex;
+            seg->fType = kQuad_SegType;
+            seg->fTValue = maxt;
+        }
+    }
+    return distance;
+}
+
+SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4],
+                           SkScalar distance, int mint, int maxt, int ptIndex) {
+    if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) {
+        SkPoint tmp[7];
+        int     halft = (mint + maxt) >> 1;
+
+        SkChopCubicAtHalf(pts, tmp);
+        distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex);
+        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;
+            Segment* seg = fSegments.append();
+            seg->fDistance = distance;
+            seg->fPtIndex = ptIndex;
+            seg->fType = kCubic_SegType;
+            seg->fTValue = maxt;
+        }
+    }
+    return distance;
+}
+
+void SkPathMeasure::buildSegments() {
+    SkPoint         pts[4];
+    int             ptIndex = fFirstPtIndex;
+    SkScalar        d, distance = 0;
+    bool            isClosed = fForceClosed;
+    bool            firstMoveTo = ptIndex < 0;
+    Segment*        seg;
+
+    fSegments.reset();
+    bool done = false;
+    do {
+        switch (fIter.next(pts)) {
+            case SkPath::kMove_Verb:
+                ptIndex += 1;
+                fPts.append(1, pts);
+                if (!firstMoveTo) {
+                    done = true;
+                    break;
+                }
+                firstMoveTo = false;
+                break;
+
+            case SkPath::kLine_Verb:
+                d = SkPoint::Distance(pts[0], pts[1]);
+                SkASSERT(d >= 0);
+                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;
+
+            case SkPath::kQuad_Verb:
+                distance = this->compute_quad_segs(pts, distance, 0,
+                                                   kMaxTValue, ptIndex);
+                fPts.append(2, pts + 1);
+                ptIndex += 2;
+                break;
+
+            case SkPath::kCubic_Verb:
+                distance = this->compute_cubic_segs(pts, distance, 0,
+                                                    kMaxTValue, ptIndex);
+                fPts.append(3, pts + 1);
+                ptIndex += 3;
+                break;
+
+            case SkPath::kClose_Verb:
+                isClosed = true;
+                break;
+                
+            case SkPath::kDone_Verb:
+                done = true;
+                break;
+        }
+    } while (!done);
+
+    fLength = distance;
+    fIsClosed = isClosed;
+    fFirstPtIndex = ptIndex;
+
+#ifdef SK_DEBUG
+    {
+        const Segment* seg = fSegments.begin();
+        const Segment* stop = fSegments.end();
+        unsigned        ptIndex = 0;
+        SkScalar        distance = 0;
+
+        while (seg < stop) {
+            SkASSERT(seg->fDistance > distance);
+            SkASSERT(seg->fPtIndex >= ptIndex);
+            SkASSERT(seg->fTValue > 0);
+
+            const Segment* s = seg;
+            while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) {
+                SkASSERT(s[0].fType == s[1].fType);
+                SkASSERT(s[0].fTValue < s[1].fTValue);
+                s += 1;
+            }
+
+            distance = seg->fDistance;
+            ptIndex = seg->fPtIndex;
+            seg += 1;
+        }
+    //  SkDebugf("\n");
+    }
+#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];
+
+    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));
+            }
+            if (tangent) {
+                tangent->setNormalize(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY);
+            }
+            break;
+        case kQuad_SegType:
+            SkEvalQuadAt(pts, t, pos, tangent);
+            if (tangent) {
+                tangent->normalize();
+            }
+            break;
+        case kCubic_SegType:
+            SkEvalCubicAt(pts, t, pos, tangent, NULL);
+            if (tangent) {
+                tangent->normalize();
+            }
+            break;
+        default:
+            SkDEBUGFAIL("unknown segType");
+    }
+}
+
+static void seg_to(const SkTDArray<SkPoint>& segmentPts, int ptIndex,
+                   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;
+    }
+
+    const SkPoint*  pts = &segmentPts[ptIndex];
+    SkPoint         tmp0[7], tmp1[7];
+
+    switch (segType) {
+        case kLine_SegType:
+            if (stopT == kMaxTValue) {
+                dst->lineTo(pts[1]);
+            } else {
+                dst->lineTo(SkScalarInterp(pts[0].fX, pts[1].fX, stopT),
+                            SkScalarInterp(pts[0].fY, pts[1].fY, stopT));
+            }
+            break;
+        case kQuad_SegType:
+            if (startT == 0) {
+                if (stopT == SK_Scalar1) {
+                    dst->quadTo(pts[1], pts[2]);
+                } else {
+                    SkChopQuadAt(pts, tmp0, stopT);
+                    dst->quadTo(tmp0[1], tmp0[2]);
+                }
+            } else {
+                SkChopQuadAt(pts, tmp0, startT);
+                if (stopT == SK_Scalar1) {
+                    dst->quadTo(tmp0[3], tmp0[4]);
+                } else {
+                    SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT,
+                                                         SK_Scalar1 - startT));
+                    dst->quadTo(tmp1[1], tmp1[2]);
+                }
+            }
+            break;
+        case kCubic_SegType:
+            if (startT == 0) {
+                if (stopT == SK_Scalar1) {
+                    dst->cubicTo(pts[1], pts[2], pts[3]);
+                } else {
+                    SkChopCubicAt(pts, tmp0, stopT);
+                    dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]);
+                }
+            } else {
+                SkChopCubicAt(pts, tmp0, startT);
+                if (stopT == SK_Scalar1) {
+                    dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]);
+                } else {
+                    SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT,
+                                                        SK_Scalar1 - startT));
+                    dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]);
+                }
+            }
+            break;
+        default:
+            SkDEBUGFAIL("unknown segType");
+            sk_throw();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+SkPathMeasure::SkPathMeasure() {
+    fPath = NULL;
+    fLength = -1;   // signal we need to compute it
+    fForceClosed = false;
+    fFirstPtIndex = -1;
+}
+
+SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) {
+    fPath = &path;
+    fLength = -1;   // signal we need to compute it
+    fForceClosed = forceClosed;
+    fFirstPtIndex = -1;
+
+    fIter.setPath(path, forceClosed);
+}
+
+SkPathMeasure::~SkPathMeasure() {}
+
+/** Assign a new path, or null to have none.
+*/
+void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) {
+    fPath = path;
+    fLength = -1;   // signal we need to compute it
+    fForceClosed = forceClosed;
+    fFirstPtIndex = -1;
+
+    if (path) {
+        fIter.setPath(*path, forceClosed);
+    }
+    fSegments.reset();
+    fPts.reset();
+}
+
+SkScalar SkPathMeasure::getLength() {
+    if (fPath == NULL) {
+        return 0;
+    }
+    if (fLength < 0) {
+        this->buildSegments();
+    }
+    SkASSERT(fLength >= 0);
+    return fLength;
+}
+
+const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment(
+                                            SkScalar distance, SkScalar* t) {
+    SkDEBUGCODE(SkScalar length = ) this->getLength();
+    SkASSERT(distance >= 0 && distance <= length);
+
+    const Segment*  seg = fSegments.begin();
+    int             count = fSegments.count();
+
+    int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance,
+                                    sizeof(Segment));
+    // don't care if we hit an exact match or not, so we xor index if it is negative
+    index ^= (index >> 31);
+    seg = &seg[index];
+
+    // now interpolate t-values with the prev segment (if possible)
+    SkScalar    startT = 0, startD = 0;
+    // check if the prev segment is legal, and references the same set of points
+    if (index > 0) {
+        startD = seg[-1].fDistance;
+        if (seg[-1].fPtIndex == seg->fPtIndex) {
+            SkASSERT(seg[-1].fType == seg->fType);
+            startT = seg[-1].getScalarT();
+        }
+    }
+
+    SkASSERT(seg->getScalarT() > startT);
+    SkASSERT(distance >= startD);
+    SkASSERT(seg->fDistance > startD);
+
+    *t = startT + SkScalarMulDiv(seg->getScalarT() - startT,
+                                 distance - startD,
+                                 seg->fDistance - startD);
+    return seg;
+}
+
+bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos,
+                              SkVector* tangent) {
+    SkASSERT(fPath);
+    if (fPath == NULL) {
+        return false;
+    }
+
+    SkScalar    length = this->getLength(); // call this to force computing it
+    int         count = fSegments.count();
+
+    if (count == 0 || length == 0) {
+        return false;
+    }
+
+    // pin the distance to a legal range
+    if (distance < 0) {
+        distance = 0;
+    } 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);
+    return true;
+}
+
+bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix,
+                              MatrixFlags flags) {
+    SkPoint     position;
+    SkVector    tangent;
+
+    if (this->getPosTan(distance, &position, &tangent)) {
+        if (matrix) {
+            if (flags & kGetTangent_MatrixFlag) {
+                matrix->setSinCos(tangent.fY, tangent.fX, 0, 0);
+            } else {
+                matrix->reset();
+            }
+            if (flags & kGetPosition_MatrixFlag) {
+                matrix->postTranslate(position.fX, position.fY);
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst,
+                               bool startWithMoveTo) {
+    SkASSERT(dst);
+
+    SkScalar length = this->getLength();    // ensure we have built our segments
+
+    if (startD < 0) {
+        startD = 0;
+    }
+    if (stopD > length) {
+        stopD = length;
+    }
+    if (startD >= stopD) {
+        return false;
+    }
+
+    SkPoint  p;
+    SkScalar startT, stopT;
+    const Segment* seg = this->distanceToSegment(startD, &startT);
+    const Segment* stopSeg = this->distanceToSegment(stopD, &stopT);
+    SkASSERT(seg <= stopSeg);
+
+    if (startWithMoveTo) {
+        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);
+    } else {
+        do {
+            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);
+    }
+    return true;
+}
+
+bool SkPathMeasure::isClosed() {
+    (void)this->getLength();
+    return fIsClosed;
+}
+
+/** Move to the next contour in the path. Return true if one exists, or false if
+    we're done with the path.
+*/
+bool SkPathMeasure::nextContour() {
+    fLength = -1;
+    return this->getLength() > 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkPathMeasure::dump() {
+    SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count());
+
+    for (int i = 0; i < fSegments.count(); i++) {
+        const Segment* seg = &fSegments[i];
+        SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n",
+                i, seg->fDistance, seg->fPtIndex, seg->getScalarT(),
+                 seg->fType);
+    }
+}
+
+#endif
diff --git a/legacy/src/core/SkPicture.cpp b/legacy/src/core/SkPicture.cpp
new file mode 100644
index 0000000..6aaaf2d
--- /dev/null
+++ b/legacy/src/core/SkPicture.cpp
@@ -0,0 +1,241 @@
+
+/*
+ * Copyright 2007 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 "SkPictureFlat.h"
+#include "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+
+#include "SkCanvas.h"
+#include "SkChunkAlloc.h"
+#include "SkPicture.h"
+#include "SkRegion.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTSearch.h"
+#include "SkTime.h"
+
+#include "SkReader32.h"
+#include "SkWriter32.h"
+
+#define DUMP_BUFFER_SIZE 65536
+
+//#define ENABLE_TIME_DRAW    // dumps milliseconds for each draw
+
+
+#ifdef SK_DEBUG
+// enable SK_DEBUG_TRACE to trace DrawType elements when
+//     recorded and played back
+// #define SK_DEBUG_TRACE
+// enable SK_DEBUG_SIZE to see the size of picture components
+// #define SK_DEBUG_SIZE
+// enable SK_DEBUG_DUMP to see the contents of recorded elements
+// #define SK_DEBUG_DUMP
+// enable SK_DEBUG_VALIDATE to check internal structures for consistency
+// #define SK_DEBUG_VALIDATE
+#endif
+
+#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
+const char* DrawTypeToString(DrawType drawType) {
+    switch (drawType) {
+        case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
+        case CLIP_PATH: return "CLIP_PATH";
+        case CLIP_REGION: return "CLIP_REGION";
+        case CLIP_RECT: return "CLIP_RECT";
+        case CONCAT: return "CONCAT";
+        case DRAW_BITMAP: return "DRAW_BITMAP";
+        case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
+        case DRAW_BITMAP_RECT: return "DRAW_BITMAP_RECT";
+        case DRAW_PAINT: return "DRAW_PAINT";
+        case DRAW_PATH: return "DRAW_PATH";
+        case DRAW_PICTURE: return "DRAW_PICTURE";
+        case DRAW_POINTS: return "DRAW_POINTS";
+        case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
+        case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
+        case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL";
+        case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE";
+        case DRAW_SPRITE: return "DRAW_SPRITE";
+        case DRAW_TEXT: return "DRAW_TEXT";
+        case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
+        case RESTORE: return "RESTORE";
+        case ROTATE: return "ROTATE";
+        case SAVE: return "SAVE";
+        case SAVE_LAYER: return "SAVE_LAYER";
+        case SCALE: return "SCALE";
+        case SKEW: return "SKEW";
+        case TRANSLATE: return "TRANSLATE";
+        default:
+            SkDebugf("DrawType error 0x%08x\n", drawType);
+            SkASSERT(0);
+            break;
+    }
+    SkASSERT(0);
+    return NULL;
+}
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+static void validateMatrix(const SkMatrix* matrix) {
+    SkScalar scaleX = matrix->getScaleX();
+    SkScalar scaleY = matrix->getScaleY();
+    SkScalar skewX = matrix->getSkewX();
+    SkScalar skewY = matrix->getSkewY();
+    SkScalar perspX = matrix->getPerspX();
+    SkScalar perspY = matrix->getPerspY();
+    if (scaleX != 0 && skewX != 0)
+        SkDebugf("scaleX != 0 && skewX != 0\n");
+    SkASSERT(scaleX == 0 || skewX == 0);
+    SkASSERT(scaleY == 0 || skewY == 0);
+    SkASSERT(perspX == 0);
+    SkASSERT(perspY == 0);
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPicture::SkPicture() {
+    fRecord = NULL;
+    fPlayback = NULL;
+    fWidth = fHeight = 0;
+}
+
+SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
+    fWidth = src.fWidth;
+    fHeight = src.fHeight;
+    fRecord = NULL;
+
+    /*  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 (src.fPlayback) {
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
+    } else if (src.fRecord) {
+        // here we do a fake src.endRecording()
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
+    } else {
+        fPlayback = NULL;
+    }
+}
+
+SkPicture::~SkPicture() {
+    SkSafeUnref(fRecord);
+    SkDELETE(fPlayback);
+}
+
+void SkPicture::swap(SkPicture& other) {
+    SkTSwap(fRecord, other.fRecord);
+    SkTSwap(fPlayback, other.fPlayback);
+    SkTSwap(fWidth, other.fWidth);
+    SkTSwap(fHeight, other.fHeight);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas* SkPicture::beginRecording(int width, int height,
+                                    uint32_t recordingFlags) {
+    if (fPlayback) {
+        SkDELETE(fPlayback);
+        fPlayback = NULL;
+    }
+
+    if (NULL != fRecord) {
+        fRecord->unref();
+        fRecord = NULL;
+    }
+
+    fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags));
+
+    fWidth = width;
+    fHeight = height;
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, width, height);
+    fRecord->setBitmapDevice(bm);
+
+    return fRecord;
+}
+
+SkCanvas* SkPicture::getRecordingCanvas() const {
+    // will be null if we are not recording
+    return fRecord;
+}
+
+void SkPicture::endRecording() {
+    if (NULL == fPlayback) {
+        if (NULL != fRecord) {
+            fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
+            fRecord->unref();
+            fRecord = NULL;
+        }
+    }
+    SkASSERT(NULL == fRecord);
+}
+
+void SkPicture::draw(SkCanvas* surface) {
+    this->endRecording();
+    if (fPlayback) {
+        fPlayback->draw(*surface);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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();
+    }
+
+    fWidth = stream->readU32();
+    fHeight = stream->readU32();
+
+    fRecord = NULL;
+    fPlayback = NULL;
+
+    if (stream->readBool()) {
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, pictureVersion));
+    }
+}
+
+void SkPicture::serialize(SkWStream* stream) const {
+    SkPicturePlayback* playback = fPlayback;
+
+    if (NULL == playback && fRecord) {
+        playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
+    }
+
+    stream->write32(PICTURE_VERSION_JB);
+    stream->write32(fWidth);
+    stream->write32(fHeight);
+    if (playback) {
+        stream->writeBool(true);
+        playback->serialize(stream);
+        // delete playback if it is a local version (i.e. cons'd up just now)
+        if (playback != fPlayback) {
+            SkDELETE(playback);
+        }
+    } else {
+        stream->writeBool(false);
+    }
+}
+
+void SkPicture::abortPlayback() {
+    if (NULL == fPlayback) {
+        return;
+    }
+    fPlayback->abort();
+}
+
+
diff --git a/legacy/src/core/SkPictureFlat.cpp b/legacy/src/core/SkPictureFlat.cpp
new file mode 100644
index 0000000..1058526
--- /dev/null
+++ b/legacy/src/core/SkPictureFlat.cpp
@@ -0,0 +1,264 @@
+
+/*
+ * 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 "SkPictureFlat.h"
+
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#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
+
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+
+    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() {
+    this->reset(NULL);
+}
+
+void SkRefCntPlayback::reset(const SkRefCntSet* rec) {
+    for (int i = 0; i < fCount; i++) {
+        SkASSERT(fArray[i]);
+        fArray[i]->unref();
+    }
+    SkDELETE_ARRAY(fArray);
+    
+    if (rec) {
+        fCount = rec->count();
+        fArray = SkNEW_ARRAY(SkRefCnt*, fCount);
+        rec->copyToArray(fArray);
+        for (int i = 0; i < fCount; i++) {
+            fArray[i]->ref();
+        }
+    } else {
+        fCount = 0;
+        fArray = NULL;
+    }
+}
+
+void SkRefCntPlayback::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) {
+    SkASSERT((unsigned)index < (unsigned)fCount);
+    SkRefCnt_SafeAssign(fArray[index], obj);
+    return obj;
+}
+
diff --git a/legacy/src/core/SkPictureFlat.h b/legacy/src/core/SkPictureFlat.h
new file mode 100644
index 0000000..bfd253a
--- /dev/null
+++ b/legacy/src/core/SkPictureFlat.h
@@ -0,0 +1,238 @@
+
+/*
+ * 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 SkPictureFlat_DEFINED
+#define SkPictureFlat_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkBitmap.h"
+#include "SkPicture.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+
+enum DrawType {
+    UNUSED,
+    CLIP_PATH,
+    CLIP_REGION,
+    CLIP_RECT,
+    CONCAT,
+    DRAW_BITMAP,
+    DRAW_BITMAP_MATRIX,
+    DRAW_BITMAP_RECT,
+    DRAW_CLEAR,
+    DRAW_DATA,
+    DRAW_PAINT,
+    DRAW_PATH,
+    DRAW_PICTURE,
+    DRAW_POINTS,
+    DRAW_POS_TEXT,
+    DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
+    DRAW_POS_TEXT_H,
+    DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
+    DRAW_RECT,
+    DRAW_SHAPE,
+    DRAW_SPRITE,
+    DRAW_TEXT,
+    DRAW_TEXT_ON_PATH,
+    DRAW_TEXT_TOP_BOTTOM,   // fast variant of DRAW_TEXT
+    DRAW_VERTICES,
+    RESTORE,
+    ROTATE,
+    SAVE,
+    SAVE_LAYER,
+    SCALE,
+    SET_MATRIX,
+    SKEW,
+    TRANSLATE,
+    DRAW_BITMAP_NINE
+};
+
+enum DrawVertexFlags {
+    DRAW_VERTICES_HAS_TEXS    = 0x01,
+    DRAW_VERTICES_HAS_COLORS  = 0x02,
+    DRAW_VERTICES_HAS_INDICES = 0x04
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// clipparams are packed in 5 bits
+//  doAA:1 | regionOp:4
+
+static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) {
+    unsigned doAABit = doAA ? 1 : 0;
+    return (doAABit << 4) | op;
+}
+
+static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) {
+    return (SkRegion::Op)(packed & 0xF);
+}
+
+static inline bool ClipParams_unpackDoAA(uint32_t packed) {
+    return SkToBool((packed >> 4) & 1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkRefCntPlayback {
+public:
+    SkRefCntPlayback();
+    virtual ~SkRefCntPlayback();
+    
+    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);
+    }
+    
+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) {
+        fArray = SkNEW_ARRAY(SkFlattenable::Factory, count);
+    }
+
+    ~SkFactoryPlayback() {
+        SkDELETE_ARRAY(fArray);
+    }
+    
+    SkFlattenable::Factory* base() const { return fArray; }
+
+    void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+        buffer.setFactoryPlayback(fArray, fCount);
+    }
+    
+private:
+    int fCount;
+    SkFlattenable::Factory* fArray;
+};
+
+class SkFlatData {
+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
+
+protected:
+    static SkFlatData* Alloc(SkChunkAlloc* heap, int32_t size, int index);
+    
+    int fIndex;
+    int32_t fAllocSize;
+};
+
+class SkFlatBitmap : public SkFlatData {
+public:
+    static SkFlatBitmap* Flatten(SkChunkAlloc*, const SkBitmap&, int index,
+                                 SkRefCntSet*);
+
+    void unflatten(SkBitmap* bitmap, SkRefCntPlayback* rcp) const {
+        SkFlattenableReadBuffer buffer(fBitmapData);
+        if (rcp) {
+            rcp->setupBuffer(buffer);
+        }
+        bitmap->unflatten(buffer);
+    }
+
+#ifdef SK_DEBUG_VALIDATE
+    void validate() const {
+        // to be written
+    }
+#endif
+
+private:
+    char fBitmapData[1];
+    typedef SkFlatData INHERITED;
+};
+
+class SkFlatMatrix : public SkFlatData {
+public:
+    static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index);
+
+    void unflatten(SkMatrix* result) const {
+        result->unflatten(fMatrixData);
+    }
+
+#ifdef SK_DEBUG_DUMP
+    void dump() const;
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+    void validate() const {
+        // to be written
+    }
+#endif
+
+private:
+    char fMatrixData[1];
+    typedef SkFlatData INHERITED;
+};
+
+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);
+
+#ifdef SK_DEBUG_DUMP
+    void dump() const;
+#endif
+    
+private:
+    char fPaintData[1];
+    typedef SkFlatData INHERITED;
+};
+
+class SkFlatRegion : public SkFlatData {
+public:
+    static SkFlatRegion* Flatten(SkChunkAlloc* heap, const SkRegion& region, int index);
+    
+    void unflatten(SkRegion* result) const {
+        result->unflatten(fRegionData);
+    }
+
+#ifdef SK_DEBUG_VALIDATE
+    void validate() const {
+        // to be written
+    }
+#endif
+
+private:
+    char fRegionData[1];
+    typedef SkFlatData INHERITED;
+};
+
+#endif
diff --git a/legacy/src/core/SkPicturePlayback.cpp b/legacy/src/core/SkPicturePlayback.cpp
new file mode 100644
index 0000000..f5756a3
--- /dev/null
+++ b/legacy/src/core/SkPicturePlayback.cpp
@@ -0,0 +1,1431 @@
+
+/*
+ * 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 "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+#include "SkTypeface.h"
+#include <new>
+
+/*  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).
+ */
+#define SPEW_CLIP_SKIPPINGx
+
+SkPicturePlayback::SkPicturePlayback() {
+    this->init();
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) {
+#ifdef SK_DEBUG_SIZE
+    size_t overallBytes, bitmapBytes, matricesBytes,
+    paintBytes, pathBytes, pictureBytes, regionBytes;
+    int bitmaps = record.bitmaps(&bitmapBytes);
+    int matrices = record.matrices(&matricesBytes);
+    int paints = record.paints(&paintBytes);
+    int paths = record.paths(&pathBytes);
+    int pictures = record.pictures(&pictureBytes);
+    int regions = record.regions(&regionBytes);
+    SkDebugf("picture record mem used %zd (stream %zd) ", record.size(),
+             record.streamlen());
+    if (bitmaps != 0)
+        SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
+    if (matrices != 0)
+        SkDebugf("matrices size %zd (matrices:%d) ", matricesBytes, matrices);
+    if (paints != 0)
+        SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
+    if (paths != 0)
+        SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
+    if (pictures != 0)
+        SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
+    if (regions != 0)
+        SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
+    if (record.fPointWrites != 0)
+        SkDebugf("points size %zd (points:%d) ", record.fPointBytes, record.fPointWrites);
+    if (record.fRectWrites != 0)
+        SkDebugf("rects size %zd (rects:%d) ", record.fRectBytes, record.fRectWrites);
+    if (record.fTextWrites != 0)
+        SkDebugf("text size %zd (text strings:%d) ", record.fTextBytes, record.fTextWrites);
+
+    SkDebugf("\n");
+#endif
+#ifdef SK_DEBUG_DUMP
+    record.dumpMatrices();
+    record.dumpPaints();
+#endif
+
+    record.validate();
+    const SkWriter32& writer = record.writeStream();
+    init();
+    if (writer.size() == 0)
+        return;
+
+    {
+        size_t size = writer.size();
+        void* buffer = sk_malloc_throw(size);
+        writer.flatten(buffer);
+        fReader.setMemory(buffer, size);    // fReader owns buffer now
+    }
+
+    // copy over the refcnt dictionary to our reader
+    //
+    fRCPlayback.reset(&record.fRCSet);
+    fRCPlayback.setupBuffer(fReader);
+
+    fTFPlayback.reset(&record.fTFSet);
+    fTFPlayback.setupBuffer(fReader);
+
+    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);
+        }
+    }
+
+    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]);
+        }
+    }
+
+#ifdef SK_DEBUG_SIZE
+    int overall = fPlayback->size(&overallBytes);
+    bitmaps = fPlayback->bitmaps(&bitmapBytes);
+    paints = fPlayback->paints(&paintBytes);
+    paths = fPlayback->paths(&pathBytes);
+    pictures = fPlayback->pictures(&pictureBytes);
+    regions = fPlayback->regions(&regionBytes);
+    SkDebugf("playback size %zd (objects:%d) ", overallBytes, overall);
+    if (bitmaps != 0)
+        SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
+    if (paints != 0)
+        SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
+    if (paths != 0)
+        SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
+    if (pictures != 0)
+        SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
+    if (regions != 0)
+        SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
+    SkDebugf("\n");
+#endif
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) {
+    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);
+    }
+
+    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];
+    }
+}
+
+void SkPicturePlayback::init() {
+    fBitmaps = NULL;
+    fMatrices = NULL;
+    fPaints = NULL;
+    fPathHeap = NULL;
+    fPictureRefs = NULL;
+    fRegions = NULL;
+    fBitmapCount = fMatrixCount = fPaintCount = fPictureCount =
+    fRegionCount = 0;
+
+    fFactoryPlayback = NULL;
+}
+
+SkPicturePlayback::~SkPicturePlayback() {
+    sk_free((void*) fReader.base());
+
+    SkDELETE_ARRAY(fBitmaps);
+    SkDELETE_ARRAY(fMatrices);
+    SkDELETE_ARRAY(fPaints);
+    SkDELETE_ARRAY(fRegions);
+
+    SkSafeUnref(fPathHeap);
+
+    for (int i = 0; i < fPictureCount; i++) {
+        fPictureRefs[i]->unref();
+    }
+    SkDELETE_ARRAY(fPictureRefs);
+
+    SkDELETE(fFactoryPlayback);
+}
+
+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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+// 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')
+// 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', ' ')
+
+#include "SkStream.h"
+
+static void writeTagSize(SkFlattenableWriteBuffer& buffer, uint32_t tag,
+                         uint32_t size) {
+    buffer.write32(tag);
+    buffer.write32(size);
+}
+
+static void writeTagSize(SkWStream* stream, uint32_t tag,
+                         uint32_t size) {
+    stream->write32(tag);
+    stream->write32(size);
+}
+
+static void writeFactories(SkWStream* stream, const SkFactorySet& rec) {
+    int count = rec.count();
+
+    writeTagSize(stream, PICT_FACTORY_TAG, count);
+
+    SkAutoSTMalloc<16, SkFlattenable::Factory> storage(count);
+    SkFlattenable::Factory* array = (SkFlattenable::Factory*)storage.get();
+    rec.copyToArray(array);
+
+    for (int i = 0; i < count; i++) {
+        const char* name = SkFlattenable::FactoryToName(array[i]);
+//        SkDebugf("---- write factories [%d] %p <%s>\n", i, array[i], name);
+        if (NULL == name || 0 == *name) {
+            stream->writePackedUInt(0);
+        } else {
+            uint32_t len = strlen(name);
+            stream->writePackedUInt(len);
+            stream->write(name, len);
+        }
+    }
+}
+
+static void writeTypefaces(SkWStream* stream, const SkRefCntSet& rec) {
+    int count = rec.count();
+
+    writeTagSize(stream, PICT_TYPEFACE_TAG, count);
+
+    SkAutoSTMalloc<16, SkTypeface*> storage(count);
+    SkTypeface** array = (SkTypeface**)storage.get();
+    rec.copyToArray((SkRefCnt**)array);
+
+    for (int i = 0; i < count; i++) {
+        array[i]->serialize(stream);
+    }
+}
+
+void SkPicturePlayback::serialize(SkWStream* stream) const {
+    writeTagSize(stream, PICT_READER_TAG, fReader.size());
+    stream->write(fReader.base(), fReader.size());
+
+    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);
+        }
+    }
+
+    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);
+    }
+
+    // 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);
+    }
+
+    writeTagSize(stream, PICT_ARRAYS_TAG, buffer.size());
+    buffer.writeToStream(stream);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int readTagSize(SkFlattenableReadBuffer& buffer, uint32_t expectedTag) {
+    uint32_t tag = buffer.readU32();
+    if (tag != expectedTag) {
+        sk_throw();
+    }
+    return buffer.readU32();
+}
+
+static int readTagSize(SkStream* stream, uint32_t expectedTag) {
+    uint32_t tag = stream->readU32();
+    if (tag != expectedTag) {
+        sk_throw();
+    }
+    return stream->readU32();
+}
+
+SkPicturePlayback::SkPicturePlayback(SkStream* stream, uint32_t pictureVersion) {
+    this->init();
+
+    int i;
+
+    {
+        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));
+        }
+    }
+
+    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();
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SPEW_CLIP_SKIPPING
+struct SkipClipRec {
+    int     fCount;
+    size_t  fSize;
+
+    SkipClipRec() {
+        fCount = 0;
+        fSize = 0;
+    }
+
+    void recordSkip(size_t bytes) {
+        fCount += 1;
+        fSize += bytes;
+    }
+};
+#endif
+
+void SkPicturePlayback::draw(SkCanvas& canvas) {
+#ifdef ENABLE_TIME_DRAW
+    SkAutoTime  at("SkPicture::draw", 50);
+#endif
+
+#ifdef SPEW_CLIP_SKIPPING
+    SkipClipRec skipRect, skipRegion, skipPath;
+#endif
+
+#ifdef SK_BUILD_FOR_ANDROID
+    SkAutoMutexAcquire autoMutex(fDrawMutex);
+#endif
+
+    TextContainer text;
+    fReader.rewind();
+
+    while (!fReader.eof()) {
+        switch (fReader.readInt()) {
+            case CLIP_PATH: {
+                const SkPath& path = getPath();
+                uint32_t packed = getInt();
+                SkRegion::Op op = ClipParams_unpackRegionOp(packed);
+                bool doAA = ClipParams_unpackDoAA(packed);
+                size_t offsetToRestore = getInt();
+                if (!canvas.clipPath(path, op, doAA) && offsetToRestore) {
+#ifdef SPEW_CLIP_SKIPPING
+                    skipPath.recordSkip(offsetToRestore - fReader.offset());
+#endif
+                    fReader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CLIP_REGION: {
+                const SkRegion& region = getRegion();
+                uint32_t packed = getInt();
+                SkRegion::Op op = ClipParams_unpackRegionOp(packed);
+                size_t offsetToRestore = getInt();
+                if (!canvas.clipRegion(region, op) && offsetToRestore) {
+#ifdef SPEW_CLIP_SKIPPING
+                    skipRegion.recordSkip(offsetToRestore - fReader.offset());
+#endif
+                    fReader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CLIP_RECT: {
+                const SkRect& rect = fReader.skipT<SkRect>();
+                uint32_t packed = getInt();
+                SkRegion::Op op = ClipParams_unpackRegionOp(packed);
+                bool doAA = ClipParams_unpackDoAA(packed);
+                size_t offsetToRestore = getInt();
+                if (!canvas.clipRect(rect, op, doAA) && offsetToRestore) {
+#ifdef SPEW_CLIP_SKIPPING
+                    skipRect.recordSkip(offsetToRestore - fReader.offset());
+#endif
+                    fReader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CONCAT:
+                canvas.concat(*getMatrix());
+                break;
+            case DRAW_BITMAP: {
+                const SkPaint* paint = getPaint();
+                const SkBitmap& bitmap = getBitmap();
+                const SkPoint& loc = fReader.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);
+            } break;
+            case DRAW_BITMAP_MATRIX: {
+                const SkPaint* paint = getPaint();
+                const SkBitmap& bitmap = getBitmap();
+                const SkMatrix* matrix = getMatrix();
+                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>();
+                canvas.drawBitmapNine(bitmap, src, dst, paint);
+            } break;
+            case DRAW_CLEAR:
+                canvas.clear(getInt());
+                break;
+            case DRAW_DATA: {
+                size_t length = getInt();
+                canvas.drawData(fReader.skip(length), length);
+                // skip handles padding the read out to a multiple of 4
+            } break;
+            case DRAW_PAINT:
+                canvas.drawPaint(*getPaint());
+                break;
+            case DRAW_PATH: {
+                const SkPaint& paint = *getPaint();
+                canvas.drawPath(getPath(), paint);
+            } break;
+            case DRAW_PICTURE:
+                canvas.drawPicture(getPicture());
+                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);
+                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));
+                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)) {
+                    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));
+                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 SkScalar top = *xpos++;
+                const SkScalar bottom = *xpos++;
+                const SkScalar constY = *xpos++;
+                if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+                    canvas.drawPosTextH(text.text(), text.length(), xpos,
+                                        constY, paint);
+                }
+            } break;
+            case DRAW_RECT: {
+                const SkPaint& paint = *getPaint();
+                canvas.drawRect(fReader.skipT<SkRect>(), paint);
+            } break;
+            case DRAW_SPRITE: {
+                const SkPaint* paint = getPaint();
+                const SkBitmap& bitmap = getBitmap();
+                int left = getInt();
+                int top = getInt();
+                canvas.drawSprite(bitmap, left, top, paint);
+            } break;
+            case DRAW_TEXT: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                SkScalar x = getScalar();
+                SkScalar y = getScalar();
+                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));
+                // ptr[0] == x
+                // ptr[1] == y
+                // ptr[2] == top
+                // ptr[3] == bottom
+                if (!canvas.quickRejectY(ptr[2], ptr[3],
+                                         SkCanvas::kAA_EdgeType)) {
+                    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();
+                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(
+                                                    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(
+                                                    vCount * sizeof(SkPoint));
+                }
+                if (flags & DRAW_VERTICES_HAS_COLORS) {
+                    colors = (const SkColor*)fReader.skip(
+                                                    vCount * sizeof(SkColor));
+                }
+                if (flags & DRAW_VERTICES_HAS_INDICES) {
+                    iCount = getInt();
+                    indices = (const uint16_t*)fReader.skip(
+                                                    iCount * sizeof(uint16_t));
+                }
+                canvas.drawVertices(vmode, vCount, verts, texs, colors, NULL,
+                                    indices, iCount, paint);
+            } break;
+            case RESTORE:
+                canvas.restore();
+                break;
+            case ROTATE:
+                canvas.rotate(getScalar());
+                break;
+            case SAVE:
+                canvas.save((SkCanvas::SaveFlags) getInt());
+                break;
+            case SAVE_LAYER: {
+                const SkRect* boundsPtr = getRectPtr();
+                const SkPaint* paint = getPaint();
+                canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) getInt());
+                } break;
+            case SCALE: {
+                SkScalar sx = getScalar();
+                SkScalar sy = getScalar();
+                canvas.scale(sx, sy);
+            } break;
+            case SET_MATRIX:
+                canvas.setMatrix(*getMatrix());
+                break;
+            case SKEW: {
+                SkScalar sx = getScalar();
+                SkScalar sy = getScalar();
+                canvas.skew(sx, sy);
+            } break;
+            case TRANSLATE: {
+                SkScalar dx = getScalar();
+                SkScalar dy = getScalar();
+                canvas.translate(dx, dy);
+            } break;
+            default:
+                SkASSERT(0);
+        }
+    }
+
+#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);
+    }
+#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
+int SkPicturePlayback::size(size_t* sizePtr) {
+    int objects = bitmaps(sizePtr);
+    objects += paints(sizePtr);
+    objects += paths(sizePtr);
+    objects += pictures(sizePtr);
+    objects += regions(sizePtr);
+    *sizePtr = fReader.size();
+    return objects;
+}
+
+int SkPicturePlayback::bitmaps(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fBitmapCount; index++) {
+     //   const SkBitmap& bitmap = fBitmaps[index];
+        result += sizeof(SkBitmap); // bitmap->size();
+    }
+    *size = result;
+    return fBitmapCount;
+}
+
+int SkPicturePlayback::paints(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fPaintCount; index++) {
+    //    const SkPaint& paint = fPaints[index];
+        result += sizeof(SkPaint); // paint->size();
+    }
+    *size = result;
+    return fPaintCount;
+}
+
+int SkPicturePlayback::paths(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fPathCount; index++) {
+        const SkPath& path = fPaths[index];
+        result += path.flatten(NULL);
+    }
+    *size = result;
+    return fPathCount;
+}
+
+int SkPicturePlayback::regions(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fRegionCount; index++) {
+    //    const SkRegion& region = fRegions[index];
+        result += sizeof(SkRegion); // region->size();
+    }
+    *size = result;
+    return fRegionCount;
+}
+#endif
+
+#ifdef SK_DEBUG_DUMP
+void SkPicturePlayback::dumpBitmap(const SkBitmap& bitmap) const {
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "BitmapData bitmap%p = {", &bitmap);
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "{kWidth, %d}, ", bitmap.width());
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "{kHeight, %d}, ", bitmap.height());
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "{kRowBytes, %d}, ", bitmap.rowBytes());
+//        start here;
+    SkDebugf("%s{0}};\n", pBuffer);
+}
+
+void dumpMatrix(const SkMatrix& matrix) const {
+    SkMatrix defaultMatrix;
+    defaultMatrix.reset();
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "MatrixData matrix%p = {", &matrix);
+    SkScalar scaleX = matrix.getScaleX();
+    if (scaleX != defaultMatrix.getScaleX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kScaleX, %g}, ", SkScalarToFloat(scaleX));
+    SkScalar scaleY = matrix.getScaleY();
+    if (scaleY != defaultMatrix.getScaleY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kScaleY, %g}, ", SkScalarToFloat(scaleY));
+    SkScalar skewX = matrix.getSkewX();
+    if (skewX != defaultMatrix.getSkewX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kSkewX, %g}, ", SkScalarToFloat(skewX));
+    SkScalar skewY = matrix.getSkewY();
+    if (skewY != defaultMatrix.getSkewY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kSkewY, %g}, ", SkScalarToFloat(skewY));
+    SkScalar translateX = matrix.getTranslateX();
+    if (translateX != defaultMatrix.getTranslateX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTranslateX, %g}, ", SkScalarToFloat(translateX));
+    SkScalar translateY = matrix.getTranslateY();
+    if (translateY != defaultMatrix.getTranslateY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTranslateY, %g}, ", SkScalarToFloat(translateY));
+    SkScalar perspX = matrix.getPerspX();
+    if (perspX != defaultMatrix.getPerspX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kPerspX, %g}, ", SkFractToFloat(perspX));
+    SkScalar perspY = matrix.getPerspY();
+    if (perspY != defaultMatrix.getPerspY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kPerspY, %g}, ", SkFractToFloat(perspY));
+    SkDebugf("%s{0}};\n", pBuffer);
+}
+
+void dumpPaint(const SkPaint& paint) const {
+    SkPaint defaultPaint;
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "PaintPointers paintPtrs%p = {", &paint);
+    const SkTypeface* typeface = paint.getTypeface();
+    if (typeface != defaultPaint.getTypeface())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTypeface, %p}, ", typeface);
+    const SkPathEffect* pathEffect = paint.getPathEffect();
+    if (pathEffect != defaultPaint.getPathEffect())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kPathEffect, %p}, ", pathEffect);
+    const SkShader* shader = paint.getShader();
+    if (shader != defaultPaint.getShader())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kShader, %p}, ", shader);
+    const SkXfermode* xfermode = paint.getXfermode();
+    if (xfermode != defaultPaint.getXfermode())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kXfermode, %p}, ", xfermode);
+    const SkMaskFilter* maskFilter = paint.getMaskFilter();
+    if (maskFilter != defaultPaint.getMaskFilter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kMaskFilter, %p}, ", maskFilter);
+    const SkColorFilter* colorFilter = paint.getColorFilter();
+    if (colorFilter != defaultPaint.getColorFilter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kColorFilter, %p}, ", colorFilter);
+    const SkRasterizer* rasterizer = paint.getRasterizer();
+    if (rasterizer != defaultPaint.getRasterizer())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kRasterizer, %p}, ", rasterizer);
+    const SkDrawLooper* drawLooper = paint.getLooper();
+    if (drawLooper != defaultPaint.getLooper())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kDrawLooper, %p}, ", drawLooper);
+    SkDebugf("%s{0}};\n", pBuffer);
+    bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "PaintScalars paintScalars%p = {", &paint);
+    SkScalar textSize = paint.getTextSize();
+    if (textSize != defaultPaint.getTextSize())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextSize, %g}, ", SkScalarToFloat(textSize));
+    SkScalar textScaleX = paint.getTextScaleX();
+    if (textScaleX != defaultPaint.getTextScaleX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextScaleX, %g}, ", SkScalarToFloat(textScaleX));
+    SkScalar textSkewX = paint.getTextSkewX();
+    if (textSkewX != defaultPaint.getTextSkewX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextSkewX, %g}, ", SkScalarToFloat(textSkewX));
+    SkScalar strokeWidth = paint.getStrokeWidth();
+    if (strokeWidth != defaultPaint.getStrokeWidth())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStrokeWidth, %g}, ", SkScalarToFloat(strokeWidth));
+    SkScalar strokeMiter = paint.getStrokeMiter();
+    if (strokeMiter != defaultPaint.getStrokeMiter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStrokeMiter, %g}, ", SkScalarToFloat(strokeMiter));
+    SkDebugf("%s{0}};\n", pBuffer);
+    bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "PaintInts = paintInts%p = {", &paint);
+    unsigned color = paint.getColor();
+    if (color != defaultPaint.getColor())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kColor, 0x%x}, ", color);
+    unsigned flags = paint.getFlags();
+    if (flags != defaultPaint.getFlags())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kFlags, 0x%x}, ", flags);
+    int align = paint.getTextAlign();
+    if (align != defaultPaint.getTextAlign())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kAlign, 0x%x}, ", align);
+    int strokeCap = paint.getStrokeCap();
+    if (strokeCap != defaultPaint.getStrokeCap())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStrokeCap, 0x%x}, ", strokeCap);
+    int strokeJoin = paint.getStrokeJoin();
+    if (strokeJoin != defaultPaint.getStrokeJoin())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kAlign, 0x%x}, ", strokeJoin);
+    int style = paint.getStyle();
+    if (style != defaultPaint.getStyle())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStyle, 0x%x}, ", style);
+    int textEncoding = paint.getTextEncoding();
+    if (textEncoding != defaultPaint.getTextEncoding())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextEncoding, 0x%x}, ", textEncoding);
+    SkDebugf("%s{0}};\n", pBuffer);
+
+    SkDebugf("PaintData paint%p = {paintPtrs%p, paintScalars%p, paintInts%p};\n",
+        &paint, &paint, &paint, &paint);
+}
+
+void SkPicturePlayback::dumpPath(const SkPath& path) const {
+    SkDebugf("path dump unimplemented\n");
+}
+
+void SkPicturePlayback::dumpPicture(const SkPicture& picture) const {
+    SkDebugf("picture dump unimplemented\n");
+}
+
+void SkPicturePlayback::dumpRegion(const SkRegion& region) const {
+    SkDebugf("region dump unimplemented\n");
+}
+
+int SkPicturePlayback::dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "k%s, ", DrawTypeToString(drawType));
+}
+
+int SkPicturePlayback::dumpInt(char* bufferPtr, char* buffer, char* name) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:%d, ", name, getInt());
+}
+
+int SkPicturePlayback::dumpRect(char* bufferPtr, char* buffer, char* name) {
+    const SkRect* rect = fReader.skipRect();
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:{l:%g t:%g r:%g b:%g}, ", name, SkScalarToFloat(rect.fLeft),
+        SkScalarToFloat(rect.fTop),
+        SkScalarToFloat(rect.fRight), SkScalarToFloat(rect.fBottom));
+}
+
+int SkPicturePlayback::dumpPoint(char* bufferPtr, char* buffer, char* name) {
+    SkPoint pt;
+    getPoint(&pt);
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:{x:%g y:%g}, ", name, SkScalarToFloat(pt.fX),
+        SkScalarToFloat(pt.fY));
+}
+
+void SkPicturePlayback::dumpPointArray(char** bufferPtrPtr, char* buffer, int count) {
+    char* bufferPtr = *bufferPtrPtr;
+    const SkPoint* pts = (const SkPoint*)fReadStream.getAtPos();
+    fReadStream.skip(sizeof(SkPoint) * count);
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "count:%d {", count);
+    for (int index = 0; index < count; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "{x:%g y:%g}, ", SkScalarToFloat(pts[index].fX),
+        SkScalarToFloat(pts[index].fY));
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "} ");
+    *bufferPtrPtr = bufferPtr;
+}
+
+int SkPicturePlayback::dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:%p, ", name, ptr);
+}
+
+int SkPicturePlayback::dumpRectPtr(char* bufferPtr, char* buffer, char* name) {
+    char result;
+    fReadStream.read(&result, sizeof(result));
+    if (result)
+        return dumpRect(bufferPtr, buffer, name);
+    else
+        return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+            "%s:NULL, ", name);
+}
+
+int SkPicturePlayback::dumpScalar(char* bufferPtr, char* buffer, char* name) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:%d, ", name, getScalar());
+}
+
+void SkPicturePlayback::dumpText(char** bufferPtrPtr, char* buffer) {
+    char* bufferPtr = *bufferPtrPtr;
+    int length = getInt();
+    bufferPtr += dumpDrawType(bufferPtr, buffer);
+    fReadStream.skipToAlign4();
+    char* text = (char*) fReadStream.getAtPos();
+    fReadStream.skip(length);
+    bufferPtr += dumpInt(bufferPtr, buffer, "length");
+    int limit = DUMP_BUFFER_SIZE - (bufferPtr - buffer) - 2;
+    length >>= 1;
+    if (limit > length)
+        limit = length;
+    if (limit > 0) {
+        *bufferPtr++ = '"';
+        for (int index = 0; index < limit; index++) {
+            *bufferPtr++ = *(unsigned short*) text;
+            text += sizeof(unsigned short);
+        }
+        *bufferPtr++ = '"';
+    }
+    *bufferPtrPtr = bufferPtr;
+}
+
+#define DUMP_DRAWTYPE(drawType) \
+    bufferPtr += dumpDrawType(bufferPtr, buffer, drawType)
+
+#define DUMP_INT(name) \
+    bufferPtr += dumpInt(bufferPtr, buffer, #name)
+
+#define DUMP_RECT_PTR(name) \
+    bufferPtr += dumpRectPtr(bufferPtr, buffer, #name)
+
+#define DUMP_POINT(name) \
+    bufferPtr += dumpRect(bufferPtr, buffer, #name)
+
+#define DUMP_RECT(name) \
+    bufferPtr += dumpRect(bufferPtr, buffer, #name)
+
+#define DUMP_POINT_ARRAY(count) \
+    dumpPointArray(&bufferPtr, buffer, count)
+
+#define DUMP_PTR(name, ptr) \
+    bufferPtr += dumpPtr(bufferPtr, buffer, #name, (void*) ptr)
+
+#define DUMP_SCALAR(name) \
+    bufferPtr += dumpScalar(bufferPtr, buffer, #name)
+
+#define DUMP_TEXT() \
+    dumpText(&bufferPtr, buffer)
+
+void SkPicturePlayback::dumpStream() {
+    SkDebugf("RecordStream stream = {\n");
+    DrawType drawType;
+    TextContainer text;
+    fReadStream.rewind();
+    char buffer[DUMP_BUFFER_SIZE], * bufferPtr;
+    while (fReadStream.read(&drawType, sizeof(drawType))) {
+        bufferPtr = buffer;
+        DUMP_DRAWTYPE(drawType);
+        switch (drawType) {
+            case CLIP_PATH: {
+                DUMP_PTR(SkPath, &getPath());
+                DUMP_INT(SkRegion::Op);
+                DUMP_INT(offsetToRestore);
+                } break;
+            case CLIP_REGION: {
+                DUMP_PTR(SkRegion, &getRegion());
+                DUMP_INT(SkRegion::Op);
+                DUMP_INT(offsetToRestore);
+            } break;
+            case CLIP_RECT: {
+                DUMP_RECT(rect);
+                DUMP_INT(SkRegion::Op);
+                DUMP_INT(offsetToRestore);
+                } break;
+            case CONCAT:
+                DUMP_PTR(SkMatrix, getMatrix());
+                break;
+            case DRAW_BITMAP: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_PTR(SkBitmap, &getBitmap());
+                DUMP_SCALAR(left);
+                DUMP_SCALAR(top);
+                } break;
+            case DRAW_PAINT:
+                DUMP_PTR(SkPaint, getPaint());
+                break;
+            case DRAW_PATH: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_PTR(SkPath, &getPath());
+                } break;
+            case DRAW_PICTURE: {
+                DUMP_PTR(SkPicture, &getPicture());
+                } break;
+            case DRAW_POINTS: {
+                DUMP_PTR(SkPaint, getPaint());
+                (void)getInt(); // PointMode
+                size_t count = getInt();
+                fReadStream.skipToAlign4();
+                DUMP_POINT_ARRAY(count);
+                } break;
+            case DRAW_POS_TEXT: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                size_t points = getInt();
+                fReadStream.skipToAlign4();
+                DUMP_POINT_ARRAY(points);
+                } break;
+            case DRAW_POS_TEXT_H: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                size_t points = getInt();
+                fReadStream.skipToAlign4();
+                DUMP_SCALAR(top);
+                DUMP_SCALAR(bottom);
+                DUMP_SCALAR(constY);
+                DUMP_POINT_ARRAY(points);
+                } break;
+            case DRAW_RECT: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_RECT(rect);
+                } break;
+            case DRAW_SPRITE: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_PTR(SkBitmap, &getBitmap());
+                DUMP_SCALAR(left);
+                DUMP_SCALAR(top);
+                } break;
+            case DRAW_TEXT: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                DUMP_SCALAR(x);
+                DUMP_SCALAR(y);
+                } break;
+            case DRAW_TEXT_ON_PATH: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                DUMP_PTR(SkPath, &getPath());
+                DUMP_PTR(SkMatrix, getMatrix());
+                } break;
+            case RESTORE:
+                break;
+            case ROTATE:
+                DUMP_SCALAR(rotate);
+                break;
+            case SAVE:
+                DUMP_INT(SkCanvas::SaveFlags);
+                break;
+            case SAVE_LAYER: {
+                DUMP_RECT_PTR(layer);
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_INT(SkCanvas::SaveFlags);
+                } break;
+            case SCALE: {
+                DUMP_SCALAR(sx);
+                DUMP_SCALAR(sy);
+                } break;
+            case SKEW: {
+                DUMP_SCALAR(sx);
+                DUMP_SCALAR(sy);
+                } break;
+            case TRANSLATE: {
+                DUMP_SCALAR(dx);
+                DUMP_SCALAR(dy);
+                } break;
+            default:
+                SkASSERT(0);
+        }
+        SkDebugf("%s\n", buffer);
+    }
+}
+
+void SkPicturePlayback::dump() const {
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    int index;
+    if (fBitmapCount > 0)
+        SkDebugf("// bitmaps (%d)\n", fBitmapCount);
+    for (index = 0; index < fBitmapCount; index++) {
+        const SkBitmap& bitmap = fBitmaps[index];
+        dumpBitmap(bitmap);
+    }
+    if (fBitmapCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "Bitmaps bitmaps = {");
+    for (index = 0; index < fBitmapCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "bitmap%p, ", &fBitmaps[index]);
+    if (fBitmapCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    if (fMatrixCount > 0)
+        SkDebugf("// matrices (%d)\n", fMatrixCount);
+    for (index = 0; index < fMatrixCount; index++) {
+        const SkMatrix& matrix = fMatrices[index];
+        dumpMatrix(matrix);
+    }
+    bufferPtr = pBuffer;
+    if (fMatrixCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "Matrices matrices = {");
+    for (index = 0; index < fMatrixCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "matrix%p, ", &fMatrices[index]);
+    if (fMatrixCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    if (fPaintCount > 0)
+        SkDebugf("// paints (%d)\n", fPaintCount);
+    for (index = 0; index < fPaintCount; index++) {
+        const SkPaint& paint = fPaints[index];
+        dumpPaint(paint);
+    }
+    bufferPtr = pBuffer;
+    if (fPaintCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "Paints paints = {");
+    for (index = 0; index < fPaintCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "paint%p, ", &fPaints[index]);
+    if (fPaintCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    for (index = 0; index < fPathCount; index++) {
+        const SkPath& path = fPaths[index];
+        dumpPath(path);
+    }
+    bufferPtr = pBuffer;
+    if (fPathCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "Paths paths = {");
+    for (index = 0; index < fPathCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "path%p, ", &fPaths[index]);
+    if (fPathCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    for (index = 0; index < fPictureCount; index++) {
+        dumpPicture(*fPictureRefs[index]);
+    }
+    bufferPtr = pBuffer;
+    if (fPictureCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "Pictures pictures = {");
+    for (index = 0; index < fPictureCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "picture%p, ", fPictureRefs[index]);
+    if (fPictureCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    for (index = 0; index < fRegionCount; index++) {
+        const SkRegion& region = fRegions[index];
+        dumpRegion(region);
+    }
+    bufferPtr = pBuffer;
+    if (fRegionCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "Regions regions = {");
+    for (index = 0; index < fRegionCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "region%p, ", &fRegions[index]);
+    if (fRegionCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    const_cast<SkPicturePlayback*>(this)->dumpStream();
+}
+
+#endif
diff --git a/legacy/src/core/SkPicturePlayback.h b/legacy/src/core/SkPicturePlayback.h
new file mode 100644
index 0000000..70bd0ce
--- /dev/null
+++ b/legacy/src/core/SkPicturePlayback.h
@@ -0,0 +1,182 @@
+
+/*
+ * 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 SkPicturePlayback_DEFINED
+#define SkPicturePlayback_DEFINED
+
+#include "SkPicture.h"
+#include "SkReader32.h"
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPathHeap.h"
+#include "SkRegion.h"
+#include "SkPictureFlat.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkThread.h"
+#endif
+
+class SkPictureRecord;
+class SkStream;
+class SkWStream;
+
+class SkPicturePlayback {
+public:
+    SkPicturePlayback();
+    SkPicturePlayback(const SkPicturePlayback& src);
+    explicit SkPicturePlayback(const SkPictureRecord& record);
+    explicit SkPicturePlayback(SkStream*, uint32_t pictureVersion = PICTURE_VERSION_JB);
+
+    virtual ~SkPicturePlayback();
+
+    void draw(SkCanvas& canvas);
+
+    void serialize(SkWStream*) const;
+
+    void dumpSize() const;
+
+    // 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();
+
+private:
+
+    class TextContainer {
+    public:
+        size_t length() { return fByteLength; }
+        const void* text() { return (const void*) fText; }
+        size_t fByteLength;
+        const char* fText;
+    };
+
+    const SkBitmap& getBitmap() {
+        int index = getInt();
+        SkASSERT(index > 0);
+        return fBitmaps[index - 1];
+    }
+
+    int getIndex() { return fReader.readInt(); }
+    int getInt() { return fReader.readInt(); }
+
+    const SkMatrix* getMatrix() {
+        int index = getInt();
+        if (index == 0) {
+            return NULL;
+        }
+        SkASSERT(index > 0 && index <= fMatrixCount);
+        return &fMatrices[index - 1];
+    }
+
+    const SkPath& getPath() {
+        return (*fPathHeap)[getInt() - 1];
+    }
+
+    SkPicture& getPicture() {
+        int index = getInt();
+        SkASSERT(index > 0 && index <= fPictureCount);
+        return *fPictureRefs[index - 1];
+    }
+
+    const SkPaint* getPaint() {
+        int index = getInt();
+        if (index == 0) {
+            return NULL;
+        }
+        SkASSERT(index > 0 && index <= fPaintCount);
+        return &fPaints[index - 1];
+    }
+
+    const SkRect* getRectPtr() {
+        if (fReader.readBool()) {
+            return &fReader.skipT<SkRect>();
+        } else {
+            return NULL;
+        }
+    }
+
+    const SkIRect* getIRectPtr() {
+        if (fReader.readBool()) {
+            return &fReader.skipT<SkIRect>();
+        } else {
+            return NULL;
+        }
+    }
+
+    const SkRegion& getRegion() {
+        int index = getInt();
+        SkASSERT(index > 0);
+        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 init();
+
+#ifdef SK_DEBUG_SIZE
+public:
+    int size(size_t* sizePtr);
+    int bitmaps(size_t* size);
+    int paints(size_t* size);
+    int paths(size_t* size);
+    int regions(size_t* size);
+#endif
+
+#ifdef SK_DEBUG_DUMP
+private:
+    void dumpBitmap(const SkBitmap& bitmap) const;
+    void dumpMatrix(const SkMatrix& matrix) const;
+    void dumpPaint(const SkPaint& paint) const;
+    void dumpPath(const SkPath& path) const;
+    void dumpPicture(const SkPicture& picture) const;
+    void dumpRegion(const SkRegion& region) const;
+    int dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType);
+    int dumpInt(char* bufferPtr, char* buffer, char* name);
+    int dumpRect(char* bufferPtr, char* buffer, char* name);
+    int dumpPoint(char* bufferPtr, char* buffer, char* name);
+    void dumpPointArray(char** bufferPtrPtr, char* buffer, int count);
+    int dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr);
+    int dumpRectPtr(char* bufferPtr, char* buffer, char* name);
+    int dumpScalar(char* bufferPtr, char* buffer, char* name);
+    void dumpText(char** bufferPtrPtr, char* buffer);
+    void dumpStream();
+
+public:
+    void dump() const;
+#endif
+
+private:
+    SkPathHeap* fPathHeap;  // reference counted
+    SkBitmap* fBitmaps;
+    int fBitmapCount;
+    SkMatrix* fMatrices;
+    int fMatrixCount;
+    SkPaint* fPaints;
+    int fPaintCount;
+    SkRegion* fRegions;
+    int fRegionCount;
+    mutable SkFlattenableReadBuffer fReader;
+
+    SkPicture** fPictureRefs;
+    int fPictureCount;
+
+    SkRefCntPlayback fRCPlayback;
+    SkTypefacePlayback fTFPlayback;
+    SkFactoryPlayback*   fFactoryPlayback;
+#ifdef SK_BUILD_FOR_ANDROID
+    SkMutex fDrawMutex;
+#endif
+};
+
+#endif
diff --git a/legacy/src/core/SkPictureRecord.cpp b/legacy/src/core/SkPictureRecord.cpp
new file mode 100644
index 0000000..fdeec3f
--- /dev/null
+++ b/legacy/src/core/SkPictureRecord.cpp
@@ -0,0 +1,815 @@
+
+/*
+ * 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 "SkPictureRecord.h"
+#include "SkTSearch.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;
+#ifdef SK_DEBUG_SIZE
+    fPointBytes = fRectBytes = fTextBytes = 0;
+    fPointWrites = fRectWrites = fTextWrites = 0;
+#endif
+
+    fRestoreOffsetStack.setReserve(32);
+    fRestoreOffsetStack.push(0);
+
+    fPathHeap = NULL;   // lazy allocate
+    fFirstSavedLayerIndex = kNoSavedLayerIndex;
+}
+
+SkPictureRecord::~SkPictureRecord() {
+    reset();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkPictureRecord::save(SaveFlags flags) {
+    addDraw(SAVE);
+    addInt(flags);
+
+    fRestoreOffsetStack.push(0);
+
+    validate();
+    return this->INHERITED::save(flags);
+}
+
+int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                               SaveFlags flags) {
+    addDraw(SAVE_LAYER);
+    addRectPtr(bounds);
+    addPaintPtr(paint);
+    addInt(flags);
+
+    fRestoreOffsetStack.push(0);
+
+    if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
+        fFirstSavedLayerIndex = fRestoreOffsetStack.count();
+    }
+
+    validate();
+    /*  Don't actually call saveLayer, because that will try to allocate an
+        offscreen device (potentially very big) which we don't actually need
+        at this time (and may not be able to afford since during record our
+        clip starts out the size of the picture, which is often much larger
+        than the size of the actual device we'll use during playback).
+     */
+    int count = this->INHERITED::save(flags);
+    this->clipRectBounds(bounds, flags, NULL);
+    return count;
+}
+
+bool SkPictureRecord::isDrawingToLayer() const {
+    return fFirstSavedLayerIndex != kNoSavedLayerIndex;
+}
+
+void SkPictureRecord::restore() {
+    // 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;
+    }
+
+    fRestoreOffsetStack.pop();
+
+    addDraw(RESTORE);
+    validate();
+    return this->INHERITED::restore();
+}
+
+bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
+    addDraw(TRANSLATE);
+    addScalar(dx);
+    addScalar(dy);
+    validate();
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
+    addDraw(SCALE);
+    addScalar(sx);
+    addScalar(sy);
+    validate();
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkPictureRecord::rotate(SkScalar degrees) {
+    addDraw(ROTATE);
+    addScalar(degrees);
+    validate();
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
+    addDraw(SKEW);
+    addScalar(sx);
+    addScalar(sy);
+    validate();
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkPictureRecord::concat(const SkMatrix& matrix) {
+    validate();
+    addDraw(CONCAT);
+    addMatrix(matrix);
+    validate();
+    return this->INHERITED::concat(matrix);
+}
+
+void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
+    validate();
+    addDraw(SET_MATRIX);
+    addMatrix(matrix);
+    validate();
+    this->INHERITED::setMatrix(matrix);
+}
+
+static bool regionOpExpands(SkRegion::Op op) {
+    switch (op) {
+        case SkRegion::kUnion_Op:
+        case SkRegion::kXOR_Op:
+        case SkRegion::kReverseDifference_Op:
+        case SkRegion::kReplace_Op:
+            return true;
+        case SkRegion::kIntersect_Op:
+        case SkRegion::kDifference_Op:
+            return false;
+        default:
+            SkDEBUGFAIL("unknown region op");
+            return false;
+    }
+}
+
+void SkPictureRecord::recordOffsetForRestore(SkRegion::Op op) {
+    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;
+        }
+    }
+
+    size_t offset = fWriter.size();
+    addInt(fRestoreOffsetStack.top());
+    fRestoreOffsetStack.top() = offset;
+}
+
+bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    addDraw(CLIP_RECT);
+    addRect(rect);
+    addInt(ClipParams_pack(op, doAA));
+
+    this->recordOffsetForRestore(op);
+
+    validate();
+    return this->INHERITED::clipRect(rect, op, doAA);
+}
+
+bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    addDraw(CLIP_PATH);
+    addPath(path);
+    addInt(ClipParams_pack(op, doAA));
+
+    this->recordOffsetForRestore(op);
+
+    validate();
+
+    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
+        return this->INHERITED::clipRect(path.getBounds(), op, doAA);
+    } else {
+        return this->INHERITED::clipPath(path, op, doAA);
+    }
+}
+
+bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
+    addDraw(CLIP_REGION);
+    addRegion(region);
+    addInt(ClipParams_pack(op, false));
+
+    this->recordOffsetForRestore(op);
+
+    validate();
+    return this->INHERITED::clipRegion(region, op);
+}
+
+void SkPictureRecord::clear(SkColor color) {
+    addDraw(DRAW_CLEAR);
+    addInt(color);
+    validate();
+}
+
+void SkPictureRecord::drawPaint(const SkPaint& paint) {
+    addDraw(DRAW_PAINT);
+    addPaint(paint);
+    validate();
+}
+
+void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                        const SkPaint& paint) {
+    addDraw(DRAW_POINTS);
+    addPaint(paint);
+    addInt(mode);
+    addInt(count);
+    fWriter.writeMul4(pts, count * sizeof(SkPoint));
+    validate();
+}
+
+void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
+    addDraw(DRAW_RECT);
+    addPaint(paint);
+    addRect(rect);
+    validate();
+}
+
+void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
+    addDraw(DRAW_PATH);
+    addPaint(paint);
+    addPath(path);
+    validate();
+}
+
+void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                        const SkPaint* paint = NULL) {
+    addDraw(DRAW_BITMAP);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addScalar(left);
+    addScalar(top);
+    validate();
+}
+
+void SkPictureRecord::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                            const SkRect& dst, const SkPaint* paint) {
+    addDraw(DRAW_BITMAP_RECT);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addIRectPtr(src);  // may be null
+    addRect(dst);
+    validate();
+}
+
+void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+                                       const SkPaint* paint) {
+    addDraw(DRAW_BITMAP_MATRIX);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addMatrix(matrix);
+    validate();
+}
+
+void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                     const SkRect& dst, const SkPaint* paint) {
+    addDraw(DRAW_BITMAP_NINE);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addIRect(center);
+    addRect(dst);
+    validate();
+}
+
+void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
+                        const SkPaint* paint = NULL) {
+    addDraw(DRAW_SPRITE);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addInt(left);
+    addInt(top);
+    validate();
+}
+
+void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
+                                              SkScalar minY, SkScalar maxY) {
+    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);
+    (void)paint.computeFastBounds(bounds, &bounds);
+    // now record the top and bottom
+    addScalar(bounds.fTop);
+    addScalar(bounds.fBottom);
+}
+
+void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
+                      SkScalar y, const SkPaint& paint) {
+    bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
+
+    addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
+    addPaint(paint);
+    addText(text, byteLength);
+    addScalar(x);
+    addScalar(y);
+    if (fast) {
+        addFontMetricsTopBottom(paint, y, y);
+    }
+    validate();
+}
+
+void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
+                         const SkPoint pos[], const SkPaint& paint) {
+    size_t points = paint.countText(text, byteLength);
+    if (0 == points)
+        return;
+
+    bool canUseDrawH = true;
+    SkScalar minY = pos[0].fY;
+    SkScalar maxY = pos[0].fY;
+    // check if the caller really should have used drawPosTextH()
+    {
+        const SkScalar firstY = pos[0].fY;
+        for (size_t index = 1; index < points; index++) {
+            if (pos[index].fY != firstY) {
+                canUseDrawH = false;
+                if (pos[index].fY < minY) {
+                    minY = pos[index].fY;
+                } else if (pos[index].fY > maxY) {
+                    maxY = pos[index].fY;
+                }
+            }
+        }
+    }
+
+    bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
+    bool fast = canUseDrawH && fastBounds;
+
+    if (fast) {
+        addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
+    } else if (canUseDrawH) {
+        addDraw(DRAW_POS_TEXT_H);
+    } else if (fastBounds) {
+        addDraw(DRAW_POS_TEXT_TOP_BOTTOM);
+    } else {
+        addDraw(DRAW_POS_TEXT);
+    }
+    addPaint(paint);
+    addText(text, byteLength);
+    addInt(points);
+
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    if (canUseDrawH) {
+        if (fast) {
+            addFontMetricsTopBottom(paint, pos[0].fY, pos[0].fY);
+        }
+        addScalar(pos[0].fY);
+        SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
+        for (size_t index = 0; index < points; index++)
+            *xptr++ = pos[index].fX;
+    }
+    else {
+        fWriter.writeMul4(pos, points * sizeof(SkPoint));
+        if (fastBounds) {
+            addFontMetricsTopBottom(paint, minY, maxY);
+        }
+    }
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += fWriter.size() - start;
+    fPointWrites += points;
+#endif
+    validate();
+}
+
+void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
+                          const SkScalar xpos[], SkScalar constY,
+                          const SkPaint& paint) {
+    size_t points = paint.countText(text, byteLength);
+    if (0 == points)
+        return;
+
+    bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
+
+    addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
+    addPaint(paint);
+    addText(text, byteLength);
+    addInt(points);
+
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    if (fast) {
+        addFontMetricsTopBottom(paint, constY, constY);
+    }
+    addScalar(constY);
+    fWriter.writeMul4(xpos, points * sizeof(SkScalar));
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += fWriter.size() - start;
+    fPointWrites += points;
+#endif
+    validate();
+}
+
+void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
+                            const SkPath& path, const SkMatrix* matrix,
+                            const SkPaint& paint) {
+    addDraw(DRAW_TEXT_ON_PATH);
+    addPaint(paint);
+    addText(text, byteLength);
+    addPath(path);
+    addMatrixPtr(matrix);
+    validate();
+}
+
+void SkPictureRecord::drawPicture(SkPicture& picture) {
+    addDraw(DRAW_PICTURE);
+    addPicture(picture);
+    validate();
+}
+
+void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
+                          const SkPoint vertices[], const SkPoint texs[],
+                          const SkColor colors[], SkXfermode*,
+                          const uint16_t indices[], int indexCount,
+                          const SkPaint& paint) {
+    uint32_t flags = 0;
+    if (texs) {
+        flags |= DRAW_VERTICES_HAS_TEXS;
+    }
+    if (colors) {
+        flags |= DRAW_VERTICES_HAS_COLORS;
+    }
+    if (indexCount > 0) {
+        flags |= DRAW_VERTICES_HAS_INDICES;
+    }
+
+    addDraw(DRAW_VERTICES);
+    addPaint(paint);
+    addInt(flags);
+    addInt(vmode);
+    addInt(vertexCount);
+    addPoints(vertices, vertexCount);
+    if (flags & DRAW_VERTICES_HAS_TEXS) {
+        addPoints(texs, vertexCount);
+    }
+    if (flags & DRAW_VERTICES_HAS_COLORS) {
+        fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
+    }
+    if (flags & DRAW_VERTICES_HAS_INDICES) {
+        addInt(indexCount);
+        fWriter.writePad(indices, indexCount * sizeof(uint16_t));
+    }
+}
+
+void SkPictureRecord::drawData(const void* data, size_t length) {
+    addDraw(DRAW_DATA);
+    addInt(length);
+    fWriter.writePad(data, length);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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));
+}
+
+void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
+    addMatrixPtr(&matrix);
+}
+
+void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
+    addInt(find(fMatrices, matrix));
+}
+
+void SkPictureRecord::addPaint(const SkPaint& paint) {
+    addPaintPtr(&paint);
+}
+
+void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
+    addInt(find(fPaints, paint));
+}
+
+void SkPictureRecord::addPath(const SkPath& path) {
+    if (NULL == fPathHeap) {
+        fPathHeap = SkNEW(SkPathHeap);
+    }
+    addInt(fPathHeap->append(path));
+}
+
+void SkPictureRecord::addPicture(SkPicture& picture) {
+    int index = fPictureRefs.find(&picture);
+    if (index < 0) {    // not found
+        index = fPictureRefs.count();
+        *fPictureRefs.append() = &picture;
+        picture.ref();
+    }
+    // follow the convention of recording a 1-based index
+    addInt(index + 1);
+}
+
+void SkPictureRecord::addPoint(const SkPoint& point) {
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    fWriter.writePoint(point);
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += fWriter.size() - start;
+    fPointWrites++;
+#endif
+}
+
+void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
+    fWriter.writeMul4(pts, count * sizeof(SkPoint));
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += count * sizeof(SkPoint);
+    fPointWrites++;
+#endif
+}
+
+void SkPictureRecord::addRect(const SkRect& rect) {
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    fWriter.writeRect(rect);
+#ifdef SK_DEBUG_SIZE
+    fRectBytes += fWriter.size() - start;
+    fRectWrites++;
+#endif
+}
+
+void SkPictureRecord::addRectPtr(const SkRect* rect) {
+    if (fWriter.writeBool(rect != NULL)) {
+        fWriter.writeRect(*rect);
+    }
+}
+
+void SkPictureRecord::addIRect(const SkIRect& rect) {
+    fWriter.write(&rect, sizeof(rect));
+}
+
+void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
+    if (fWriter.writeBool(rect != NULL)) {
+        *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
+    }
+}
+
+void SkPictureRecord::addRegion(const SkRegion& region) {
+    addInt(find(fRegions, region));
+}
+
+void SkPictureRecord::addText(const void* text, size_t byteLength) {
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    addInt(byteLength);
+    fWriter.writePad(text, byteLength);
+#ifdef SK_DEBUG_SIZE
+    fTextBytes += fWriter.size() - start;
+    fTextWrites++;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+    size_t sizeData;
+    bitmaps(&sizeData);
+    result += sizeData;
+    matrices(&sizeData);
+    result += sizeData;
+    paints(&sizeData);
+    result += sizeData;
+    paths(&sizeData);
+    result += sizeData;
+    pictures(&sizeData);
+    result += sizeData;
+    regions(&sizeData);
+    result += sizeData;
+    result += streamlen();
+    return result;
+}
+
+int SkPictureRecord::bitmaps(size_t* size) const {
+    size_t result = 0;
+    int count = fBitmaps.count();
+    for (int index = 0; index < count; index++)
+        result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
+    *size = result;
+    return count;
+}
+
+int SkPictureRecord::matrices(size_t* size) const {
+    int count = fMatrices.count();
+    *size = sizeof(fMatrices[0]) * count;
+    return count;
+}
+
+int SkPictureRecord::paints(size_t* size) const {
+    size_t result = 0;
+    int count = fPaints.count();
+    for (int index = 0; index < count; index++)
+        result += sizeof(fPaints[index]) + fPaints[index]->size();
+    *size = result;
+    return count;
+}
+
+int SkPictureRecord::paths(size_t* size) const {
+    size_t result = 0;
+    int count = fPaths.count();
+    for (int index = 0; index < count; index++)
+        result += sizeof(fPaths[index]) + fPaths[index]->size();
+    *size = result;
+    return count;
+}
+
+int SkPictureRecord::regions(size_t* size) const {
+    size_t result = 0;
+    int count = fRegions.count();
+    for (int index = 0; index < count; index++)
+        result += sizeof(fRegions[index]) + fRegions[index]->size();
+    *size = result;
+    return count;
+}
+
+size_t SkPictureRecord::streamlen() const {
+    return fWriter.size();
+}
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+void SkPictureRecord::validate() const {
+    validateBitmaps();
+    validateMatrices();
+    validatePaints();
+    validatePaths();
+    validatePictures();
+    validateRegions();
+}
+
+void SkPictureRecord::validateBitmaps() const {
+    int count = fBitmaps.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatBitmap* bitPtr = fBitmaps[index];
+        SkASSERT(bitPtr);
+        bitPtr->validate();
+    }
+}
+
+void SkPictureRecord::validateMatrices() const {
+    int count = fMatrices.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatMatrix* matrix = fMatrices[index];
+        SkASSERT(matrix);
+        matrix->validate();
+    }
+}
+
+void SkPictureRecord::validatePaints() const {
+    int count = fPaints.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatPaint* paint = fPaints[index];
+        SkASSERT(paint);
+//            paint->validate();
+    }
+}
+
+void SkPictureRecord::validatePaths() const {
+    int count = fPaths.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatPath* path = fPaths[index];
+        SkASSERT(path);
+        path->validate();
+    }
+}
+
+void SkPictureRecord::validateRegions() const {
+    int count = fRegions.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatRegion* region = fRegions[index];
+        SkASSERT(region);
+        region->validate();
+    }
+}
+#endif
+
diff --git a/legacy/src/core/SkPictureRecord.h b/legacy/src/core/SkPictureRecord.h
new file mode 100644
index 0000000..dfddffe
--- /dev/null
+++ b/legacy/src/core/SkPictureRecord.h
@@ -0,0 +1,201 @@
+
+/*
+ * 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 SkPictureRecord_DEFINED
+#define SkPictureRecord_DEFINED
+
+#include "SkCanvas.h"
+#include "SkFlattenable.h"
+#include "SkPathHeap.h"
+#include "SkPicture.h"
+#include "SkPictureFlat.h"
+#include "SkTemplates.h"
+#include "SkWriter32.h"
+
+class SkPictureRecord : public SkCanvas {
+public:
+    SkPictureRecord(uint32_t recordFlags);
+    virtual ~SkPictureRecord();
+
+    virtual int save(SaveFlags) SK_OVERRIDE;
+    virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags) 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&, 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 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 drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                  const SkPaint*) SK_OVERRIDE;
+    virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                const SkRect& dst, const SkPaint*) SK_OVERRIDE;
+    virtual void drawSprite(const SkBitmap&, int left, int top,
+                            const SkPaint*) SK_OVERRIDE;
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, const SkPaint&) SK_OVERRIDE;
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint&) SK_OVERRIDE;
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                      const SkScalar xpos[], SkScalar constY, const SkPaint&) SK_OVERRIDE;
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                            const SkPath& path, const SkMatrix* matrix,
+                                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&) SK_OVERRIDE;
+    virtual void drawData(const void*, size_t) SK_OVERRIDE;
+    virtual bool isDrawingToLayer() const SK_OVERRIDE;
+
+    void addFontMetricsTopBottom(const SkPaint& paint, 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();
+
+    const SkWriter32& writeStream() const {
+        return fWriter;
+    }
+
+private:
+    SkTDArray<uint32_t> fRestoreOffsetStack;
+    int fFirstSavedLayerIndex;
+    enum {
+        kNoSavedLayerIndex = -1
+    };
+
+    void addDraw(DrawType drawType) {
+#ifdef SK_DEBUG_TRACE
+        SkDebugf("add %s\n", DrawTypeToString(drawType));
+#endif
+        fWriter.writeInt(drawType);
+    }
+    void addInt(int value) {
+        fWriter.writeInt(value);
+    }
+    void addScalar(SkScalar scalar) {
+        fWriter.writeScalar(scalar);
+    }
+
+    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);
+    void addPath(const SkPath& path);
+    void addPicture(SkPicture& picture);
+    void addPoint(const SkPoint& point);
+    void addPoints(const SkPoint pts[], int count);
+    void addRect(const SkRect& rect);
+    void addRectPtr(const SkRect* rect);
+    void addIRect(const SkIRect& rect);
+    void addIRectPtr(const SkIRect* rect);
+    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);
+
+#ifdef SK_DEBUG_DUMP
+public:
+    void dumpMatrices();
+    void dumpPaints();
+#endif
+
+#ifdef SK_DEBUG_SIZE
+public:
+    size_t size() const;
+    int bitmaps(size_t* size) const;
+    int matrices(size_t* size) const;
+    int paints(size_t* size) const;
+    int paths(size_t* size) const;
+    int regions(size_t* size) const;
+    size_t streamlen() const;
+
+    size_t fPointBytes, fRectBytes, fTextBytes;
+    int fPointWrites, fRectWrites, fTextWrites;
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+public:
+    void validate() const;
+private:
+    void validateBitmaps() const;
+    void validateMatrices() const;
+    void validatePaints() const;
+    void validatePaths() const;
+    void validateRegions() const;
+#else
+public:
+    void validate() const {}
+#endif
+
+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;
+    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);
+
+    friend class SkPicturePlayback;
+    friend class SkPictureTester; // for unit testing
+
+    typedef SkCanvas INHERITED;
+};
+
+#endif
diff --git a/legacy/src/core/SkPixelRef.cpp b/legacy/src/core/SkPixelRef.cpp
new file mode 100644
index 0000000..1279f06
--- /dev/null
+++ b/legacy/src/core/SkPixelRef.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 "SkPixelRef.h"
+#include "SkFlattenable.h"
+#include "SkThread.h"
+
+// must be a power-of-2. undef to just use 1 mutex
+#define PIXELREF_MUTEX_RING_COUNT       32
+
+#ifdef PIXELREF_MUTEX_RING_COUNT
+    static int32_t gPixelRefMutexRingIndex;
+    static SK_DECLARE_MUTEX_ARRAY(gPixelRefMutexRing, PIXELREF_MUTEX_RING_COUNT);
+#else
+    SK_DECLARE_STATIC_MUTEX(gPixelRefMutex);
+#endif
+
+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...
+    int index = sk_atomic_inc(&gPixelRefMutexRingIndex);
+    return &gPixelRefMutexRing[index & (PIXELREF_MUTEX_RING_COUNT - 1)];
+#else
+    return &gPixelRefMutex;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern int32_t SkNextPixelRefGenerationID();
+int32_t SkNextPixelRefGenerationID() {
+    static int32_t  gPixelRefGenerationID;
+    // do a loop in case our global wraps around, as we never want to
+    // return a 0
+    int32_t genID;
+    do {
+        genID = sk_atomic_inc(&gPixelRefGenerationID) + 1;
+    } while (0 == genID);
+    return genID;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPixelRef::setMutex(SkBaseMutex* mutex) {
+    if (NULL == mutex) {
+        mutex = get_default_mutex();
+    }
+    fMutex = mutex;
+}
+
+// just need a > 0 value, so pick a funny one to aid in debugging
+#define SKPIXELREF_PRELOCKED_LOCKCOUNT     123456789
+
+SkPixelRef::SkPixelRef(SkBaseMutex* mutex) : fPreLocked(false) {
+    this->setMutex(mutex);
+    fPixels = NULL;
+    fColorTable = NULL; // we do not track ownership of this
+    fLockCount = 0;
+    fGenerationID = 0;  // signal to rebuild
+    fIsImmutable = false;
+    fPreLocked = false;
+}
+
+SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex) {
+    this->setMutex(mutex);
+    fPixels = NULL;
+    fColorTable = NULL; // we do not track ownership of this
+    fLockCount = 0;
+    fGenerationID = 0;  // signal to rebuild
+    fIsImmutable = buffer.readBool();
+    fPreLocked = false;
+}
+
+void SkPixelRef::setPreLocked(void* pixels, SkColorTable* ctable) {
+    // only call me in your constructor, otherwise fLockCount tracking can get
+    // out of sync.
+    fPixels = pixels;
+    fColorTable = ctable;
+    fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT;
+    fPreLocked = true;
+}
+
+void SkPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeBool(fIsImmutable);
+}
+
+void SkPixelRef::lockPixels() {
+    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
+
+    if (!fPreLocked) {
+        SkAutoMutexAcquire  ac(*fMutex);
+
+        if (1 == ++fLockCount) {
+            fPixels = this->onLockPixels(&fColorTable);
+        }
+    }
+}
+
+void SkPixelRef::unlockPixels() {
+    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
+    
+    if (!fPreLocked) {
+        SkAutoMutexAcquire  ac(*fMutex);
+
+        SkASSERT(fLockCount > 0);
+        if (0 == --fLockCount) {
+            this->onUnlockPixels();
+            fPixels = NULL;
+            fColorTable = NULL;
+        }
+    }
+}
+
+bool SkPixelRef::lockPixelsAreWritable() const {
+    return this->onLockPixelsAreWritable();
+}
+
+bool SkPixelRef::onLockPixelsAreWritable() const {
+    return true;
+}
+
+uint32_t SkPixelRef::getGenerationID() const {
+    if (0 == fGenerationID) {
+        fGenerationID = SkNextPixelRefGenerationID();
+    }
+    return fGenerationID;
+}
+
+void SkPixelRef::notifyPixelsChanged() {
+#ifdef SK_DEBUG
+    if (fIsImmutable) {
+        SkDebugf("========== notifyPixelsChanged called on immutable pixelref");
+    }
+#endif
+    // this signals us to recompute this next time around
+    fGenerationID = 0;
+}
+
+void SkPixelRef::setImmutable() {
+    fIsImmutable = true;
+}
+
+bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) {
+    return this->onReadPixels(dst, subset);
+}
+
+bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
+    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;
+        }
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_BUILD_FOR_ANDROID
+void SkPixelRef::globalRef(void* data) {
+    this->ref();
+}
+
+void SkPixelRef::globalUnref() {
+    this->unref();
+}
+#endif
diff --git a/legacy/src/core/SkPoint.cpp b/legacy/src/core/SkPoint.cpp
new file mode 100644
index 0000000..5747504
--- /dev/null
+++ b/legacy/src/core/SkPoint.cpp
@@ -0,0 +1,434 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkPoint.h"
+
+void SkIPoint::rotateCW(SkIPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    int32_t tmp = fX;
+    dst->fX = -fY;
+    dst->fY = tmp;
+}
+
+void SkIPoint::rotateCCW(SkIPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    int32_t tmp = fX;
+    dst->fX = fY;
+    dst->fY = -tmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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), 
+                                                   SkIntToScalar(t));
+    ((SkPoint*)((intptr_t)this + 1 * stride))->set(SkIntToScalar(l), 
+                                                   SkIntToScalar(b));
+    ((SkPoint*)((intptr_t)this + 2 * stride))->set(SkIntToScalar(r), 
+                                                   SkIntToScalar(b));
+    ((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);
+    ((SkPoint*)((intptr_t)this + 3 * stride))->set(r, t);
+}
+
+void SkPoint::rotateCW(SkPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    SkScalar tmp = fX;
+    dst->fX = -fY;
+    dst->fY = tmp;
+}
+
+void SkPoint::rotateCCW(SkPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    SkScalar tmp = fX;
+    dst->fX = fY;
+    dst->fY = -tmp;
+}
+
+void SkPoint::scale(SkScalar scale, SkPoint* dst) const {
+    SkASSERT(dst);
+    dst->set(SkScalarMul(fX, scale), SkScalarMul(fY, scale));
+}
+
+bool SkPoint::normalize() {
+    return this->setLength(fX, fY, SK_Scalar1);
+}
+
+bool SkPoint::setNormalize(SkScalar x, SkScalar y) {
+    return this->setLength(x, y, SK_Scalar1);
+}
+
+bool SkPoint::setLength(SkScalar length) {
+    return this->setLength(fX, fY, length);
+}
+
+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);
+        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);
+}
+
+bool SkPoint::setLength(float x, float y, float length) {
+    float mag2 = x * x + y * y;
+    if (mag2 > SK_ScalarNearlyZero * SK_ScalarNearlyZero) {
+        float scale = length / sk_float_sqrt(mag2);
+        fX = x * scale;
+        fY = y * scale;
+        return true;
+    }
+    return false;
+}
+
+#else
+
+#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);
+
+    // 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
+    // if nearlyzero is <= 0xB504, which should be trivial, since usually
+    // nearlyzero is a very small fixed-point value.
+    SkASSERT(SK_ScalarNearlyZero <= 0xB504);
+
+    tolSqr.set(0, SK_ScalarNearlyZero * SK_ScalarNearlyZero);
+    return tmp1 > tolSqr;
+}
+
+SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
+    Sk64    tmp1, tmp2;
+
+    tmp1.setMul(dx, dx);
+    tmp2.setMul(dy, dy);
+    tmp1.add(tmp2);
+
+    return tmp1.getSqrt();
+}
+
+#ifdef SK_DEBUGx
+static SkFixed fixlen(SkFixed x, SkFixed y) {
+    float fx = (float)x;
+    float fy = (float)y;
+
+    return (int)floorf(sqrtf(fx*fx + fy*fy) + 0.5f);
+}
+#endif
+
+static inline uint32_t squarefixed(unsigned x) {
+    x >>= 16;
+    return x*x;
+}
+
+#if 1   // Newton iter for setLength
+
+static inline unsigned invsqrt_iter(unsigned V, unsigned U) {
+    unsigned x = V * U >> 14;
+    x = x * U >> 14;
+    x = (3 << 14) - x;
+    x = (U >> 1) * x >> 14;
+    return x;
+}
+
+static const uint16_t gInvSqrt14GuessTable[] = {
+    0x4000, 0x3c57, 0x393e, 0x3695, 0x3441, 0x3235, 0x3061,
+    0x2ebd, 0x2d41, 0x2be7, 0x2aaa, 0x2987, 0x287a, 0x2780,
+    0x2698, 0x25be, 0x24f3, 0x2434, 0x2380, 0x22d6, 0x2235,
+    0x219d, 0x210c, 0x2083, 0x2000, 0x1f82, 0x1f0b, 0x1e99,
+    0x1e2b, 0x1dc2, 0x1d5d, 0x1cfc, 0x1c9f, 0x1c45, 0x1bee,
+    0x1b9b, 0x1b4a, 0x1afc, 0x1ab0, 0x1a67, 0x1a20, 0x19dc,
+    0x1999, 0x1959, 0x191a, 0x18dd, 0x18a2, 0x1868, 0x1830,
+    0x17fa, 0x17c4, 0x1791, 0x175e, 0x172d, 0x16fd, 0x16ce
+};
+
+#define BUILD_INVSQRT_TABLEx
+#ifdef BUILD_INVSQRT_TABLE
+static void build_invsqrt14_guess_table() {
+    for (int i = 8; i <= 63; i++) {
+        unsigned x = SkToU16((1 << 28) / SkSqrt32(i << 25));
+        printf("0x%x, ", x);
+    }
+    printf("\n");
+}
+#endif
+
+static unsigned fast_invsqrt(uint32_t x) {
+#ifdef BUILD_INVSQRT_TABLE
+    unsigned top2 = x >> 25;
+    SkASSERT(top2 >= 8 && top2 <= 63);
+
+    static bool gOnce;
+    if (!gOnce) {
+        build_invsqrt14_guess_table();
+        gOnce = true;
+    }
+#endif
+
+    unsigned V = x >> 14;   // make V .14
+
+    unsigned top = x >> 25;
+    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);
+}
+
+/*  We "normalize" x,y to be .14 values (so we can square them and stay 32bits.
+    Then we Newton-iterate this in .14 space to compute the invser-sqrt, and
+    scale by it at the end. The .14 space means we can execute our iterations
+    and stay in 32bits as well, making the multiplies much cheaper than calling
+    SkFixedMul.
+*/
+bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
+    if (ox == 0) {
+        if (oy == 0) {
+            return false;
+        }
+        this->set(0, SkApplySign(length, SkExtractSign(oy)));
+        return true;
+    }
+    if (oy == 0) {
+        this->set(SkApplySign(length, SkExtractSign(ox)), 0);
+        return true;
+    }
+
+    unsigned x = SkAbs32(ox);
+    unsigned y = SkAbs32(oy);
+    int zeros = SkCLZ(x | y);
+
+    // make x,y 1.14 values so our fast sqr won't overflow
+    if (zeros > 17) {
+        x <<= zeros - 17;
+        y <<= zeros - 17; 
+    } else {
+        x >>= 17 - zeros;
+        y >>= 17 - zeros;
+    }
+    SkASSERT((x | y) <= 0x7FFF);
+
+    unsigned invrt = fast_invsqrt(x*x + y*y);
+
+    x = x * invrt >> 12;
+    y = y * invrt >> 12;
+
+    if (length != SK_Fixed1) {
+        x = SkFixedMul(x, length);
+        y = SkFixedMul(y, length);
+    }
+    this->set(SkApplySign(x, SkExtractSign(ox)),
+              SkApplySign(y, SkExtractSign(oy)));
+    return true;
+}
+#else
+/*
+    Normalize x,y, and then scale them by length.
+
+    The obvious way to do this would be the following:
+        S64 tmp1, tmp2;
+        tmp1.setMul(x,x);
+        tmp2.setMul(y,y);
+        tmp1.add(tmp2);
+        len = tmp1.getSqrt();
+        x' = SkFixedDiv(x, len);
+        y' = SkFixedDiv(y, len);
+    This is fine, but slower than what we do below.
+
+    The present technique does not compute the starting length, but
+    rather fiddles with x,y iteratively, all the while checking its
+    magnitude^2 (avoiding a sqrt).
+
+    We normalize by first shifting x,y so that at least one of them
+    has bit 31 set (after taking the abs of them).
+    Then we loop, refining x,y by squaring them and comparing
+    against a very large 1.0 (1 << 28), and then adding or subtracting
+    a delta (which itself is reduced by half each time through the loop).
+    For speed we want the squaring to be with a simple integer mul. To keep
+    that from overflowing we shift our coordinates down until we are dealing
+    with at most 15 bits (2^15-1)^2 * 2 says withing 32 bits)
+    When our square is close to 1.0, we shift x,y down into fixed range.
+*/
+bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
+    if (ox == 0) {
+        if (oy == 0)
+            return false;
+        this->set(0, SkApplySign(length, SkExtractSign(oy)));
+        return true;
+    }
+    if (oy == 0) {
+        this->set(SkApplySign(length, SkExtractSign(ox)), 0);
+        return true;
+    }
+
+    SkFixed x = SkAbs32(ox);
+    SkFixed y = SkAbs32(oy);
+
+    // shift x,y so that the greater of them is 15bits (1.14 fixed point)
+    {
+        int shift = SkCLZ(x | y);
+        // make them .30
+        x <<= shift - 1;
+        y <<= shift - 1;
+    }
+
+    SkFixed dx = x;
+    SkFixed dy = y;
+
+    for (int i = 0; i < 17; i++) {
+        dx >>= 1;
+        dy >>= 1;
+
+        U32 len2 = squarefixed(x) + squarefixed(y);
+        if (len2 >> 28) {
+            x -= dx;
+            y -= dy;
+        } else {
+            x += dx;
+            y += dy;
+        }
+    }
+    x >>= 14;
+    y >>= 14;
+
+#ifdef SK_DEBUGx    // measure how far we are from unit-length
+    {
+        static int gMaxError;
+        static int gMaxDiff;
+
+        SkFixed len = fixlen(x, y);
+        int err = len - SK_Fixed1;
+        err = SkAbs32(err);
+
+        if (err > gMaxError) {
+            gMaxError = err;
+            SkDebugf("gMaxError %d\n", err);
+        }
+
+        float fx = SkAbs32(ox)/65536.0f;
+        float fy = SkAbs32(oy)/65536.0f;
+        float mag = sqrtf(fx*fx + fy*fy);
+        fx /= mag;
+        fy /= mag;
+        SkFixed xx = (int)floorf(fx * 65536 + 0.5f);
+        SkFixed yy = (int)floorf(fy * 65536 + 0.5f);
+        err = SkMax32(SkAbs32(xx-x), SkAbs32(yy-y));
+        if (err > gMaxDiff) {
+            gMaxDiff = err;
+            SkDebugf("gMaxDiff %d\n", err);
+        }
+    }
+#endif
+
+    x = SkApplySign(x, SkExtractSign(ox));
+    y = SkApplySign(y, SkExtractSign(oy));
+    if (length != SK_Fixed1) {
+        x = SkFixedMul(x, length);
+        y = SkFixedMul(y, length);
+    }
+    
+    this->set(x, y);
+    return true;
+}
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkScalar SkPoint::distanceToLineBetweenSqd(const SkPoint& a,
+                                           const SkPoint& b,
+                                           Side* side) const {
+
+    SkVector u = b - a;
+    SkVector v = *this - a;
+    
+    SkScalar uLengthSqd = u.lengthSqd();
+    SkScalar det = u.cross(v);
+    if (NULL != side) {
+        SkASSERT(-1 == SkPoint::kLeft_Side &&
+                  0 == SkPoint::kOn_Side &&
+                  1 == kRight_Side);
+        *side = (Side) SkScalarSignAsInt(det);
+    }
+    return SkScalarMulDiv(det, det, uLengthSqd);
+}
+
+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 
+    // 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
+    //       to a than b.
+    //    2. v' points along u and has magnitude less than y. c is between
+    //       a and b and the distance to the segment is the same as distance
+    //       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 
+    // 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 
+    // 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) {
+        return b.distanceToSqd(*this);
+    } else {
+        SkScalar det = u.cross(v);
+        return SkScalarMulDiv(det, det, uLengthSqd);
+    }
+}
diff --git a/legacy/src/core/SkProcSpriteBlitter.cpp b/legacy/src/core/SkProcSpriteBlitter.cpp
new file mode 100644
index 0000000..a481920
--- /dev/null
+++ b/legacy/src/core/SkProcSpriteBlitter.cpp
@@ -0,0 +1,47 @@
+
+/*
+ * 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.
+ */
+
+
+#if 0   // experimental
+
+class SkProcSpriteBlitter : public SkSpriteBlitter {
+public:
+    typedef void (*Proc)(void* dst, const void* src, int count, const SkPMColor ctable[]);
+
+    SkProcSpriteBlitter(const SkBitmap& source, Proc proc, unsigned srcShift, unsigned dstShift)
+        : SkSpriteBlitter(source), fProc(proc), fSrcShift(SkToU8(srcShift)), fDstShift(SkToU8(dstShift)) {}
+
+    virtual void blitRect(int x, int y, int width, int height)
+    {
+        size_t      dstRB = fDevice.rowBytes();
+        size_t      srcRB = fSource.rowBytes();
+        char*       dst = (char*)fDevice.getPixels() + y * dstRB + (x << fDstShift);
+        const char* src = (const char*)fSource.getPixels() + (y - fTop) * srcRB + ((x - fLeft) << fSrcShift);
+        Proc        proc = fProc;
+        const SkPMColor* ctable = NULL;
+
+        if fSource.getColorTable())
+            ctable = fSource.getColorTable()->lockColors();
+
+        while (--height >= 0)
+        {
+            proc(dst, src, width, ctable);
+            dst += dstRB;
+            src += srcRB;
+        }
+
+        if fSource.getColorTable())
+            fSource.getColorTable()->unlockColors(false);
+    }
+
+private:
+    Proc    fProc;
+    uint8_t fSrcShift, fDstShift;
+};
+
+#endif
diff --git a/legacy/src/core/SkPtrRecorder.cpp b/legacy/src/core/SkPtrRecorder.cpp
new file mode 100644
index 0000000..e3a9eee
--- /dev/null
+++ b/legacy/src/core/SkPtrRecorder.cpp
@@ -0,0 +1,76 @@
+
+/*
+ * 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 "SkPtrRecorder.h"
+#include "SkTSearch.h"
+
+void SkPtrSet::reset() {
+    Pair* p = fList.begin();
+    Pair* stop = fList.end();
+    while (p < stop) {
+        this->decPtr(p->fPtr);
+        p += 1;
+    }
+    fList.reset();
+}
+
+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);
+    if (index < 0) {
+        return 0;
+    }
+    return fList[index].fIndex;
+}
+
+uint32_t SkPtrSet::add(void* ptr) {
+    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);
+    if (index < 0) {
+        index = ~index; // turn it back into an index for insertion
+        this->incPtr(ptr);
+        pair.fIndex = count + 1;
+        *fList.insert(index) = pair;
+        return count + 1;
+    } else {
+        return fList[index].fIndex;
+    }
+}
+
+void SkPtrSet::copyToArray(void* array[]) const {
+    int count = fList.count();
+    if (count > 0) {
+        SkASSERT(array);
+        const Pair* p = fList.begin();
+        // p->fIndex is base-1, so we need to subtract to find its slot
+        for (int i = 0; i < count; i++) {
+            int index = p[i].fIndex - 1;
+            SkASSERT((unsigned)index < (unsigned)count);
+            array[index] = p[i].fPtr;
+        }
+    }
+}
+
+
diff --git a/legacy/src/core/SkQuadClipper.cpp b/legacy/src/core/SkQuadClipper.cpp
new file mode 100644
index 0000000..b79d5a2
--- /dev/null
+++ b/legacy/src/core/SkQuadClipper.cpp
@@ -0,0 +1,127 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkQuadClipper.h"
+#include "SkGeometry.h"
+
+static inline void clamp_le(SkScalar& value, SkScalar max) {
+    if (value > max) {
+        value = max;
+    }
+}
+
+static inline void clamp_ge(SkScalar& value, SkScalar min) {
+    if (value < min) {
+        value = min;
+    }
+}
+
+SkQuadClipper::SkQuadClipper() {}
+
+void SkQuadClipper::setClip(const SkIRect& clip) {
+    // conver to scalars, since that's where we'll see the points
+    fClip.set(clip);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2,
+                           SkScalar target, SkScalar* t) {
+    /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2
+     *  We solve for t, using quadratic equation, hence we have to rearrange
+     * our cooefficents to look like At^2 + Bt + C
+     */
+    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) {
+        *t = roots[0];
+        return true;
+    }
+    return false;
+}
+
+static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) {
+    return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  If we somehow returned the fact that we had to flip the pts in Y, we could
+ communicate that to setQuadratic, and then avoid having to flip it back
+ here (only to have setQuadratic do the flip again)
+ */
+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];
+        dst[1] = srcPts[1];
+        dst[2] = srcPts[0];
+        reverse = true;
+    } else {
+        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)) {
+            // take the 2nd chopped quad
+            SkChopQuadAt(dst, tmp, t);
+            dst[0] = tmp[2];
+            dst[1] = tmp[3];
+        } else {
+            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the top
+            for (int i = 0; i < 3; i++) {
+                if (dst[i].fY < ctop) {
+                    dst[i].fY = ctop;
+                }
+            }
+        }
+    }
+    
+    // are we partially below
+    if (dst[2].fY > cbot) {
+        if (chopMonoQuadAtY(dst, cbot, &t)) {
+            SkChopQuadAt(dst, tmp, t);
+            dst[1] = tmp[1];
+            dst[2] = tmp[2];
+        } else {
+            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
+            // so we just clamp against the bottom
+            for (int i = 0; i < 3; i++) {
+                if (dst[i].fY > cbot) {
+                    dst[i].fY = cbot;
+                }
+            }
+        }
+    }
+    
+    if (reverse) {
+        SkTSwap<SkPoint>(dst[0], dst[2]);
+    }
+    return true;
+}
+
diff --git a/legacy/src/core/SkQuadClipper.h b/legacy/src/core/SkQuadClipper.h
new file mode 100644
index 0000000..be0cea0
--- /dev/null
+++ b/legacy/src/core/SkQuadClipper.h
@@ -0,0 +1,70 @@
+
+/*
+ * 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 SkQuadClipper_DEFINED
+#define SkQuadClipper_DEFINED
+
+#include "SkPath.h"
+
+/** 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:
+    SkRect      fClip;
+};
+
+/** Iterator that returns the clipped segements of a quad clipped to a rect.
+    The segments will be either lines or quads (based on SkPath::Verb), and
+    will all be monotonic in Y
+ */
+class SkQuadClipper2 {
+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/legacy/src/core/SkRasterClip.cpp b/legacy/src/core/SkRasterClip.cpp
new file mode 100644
index 0000000..5a2447d
--- /dev/null
+++ b/legacy/src/core/SkRasterClip.cpp
@@ -0,0 +1,272 @@
+/*
+ * 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 "SkRasterClip.h"
+
+
+SkRasterClip::SkRasterClip() {
+    fIsBW = true;
+}
+
+SkRasterClip::SkRasterClip(const SkRasterClip& src) {
+    AUTO_RASTERCLIP_VALIDATE(src);
+    
+    fIsBW = src.fIsBW;
+    if (fIsBW) {
+        fBW = src.fBW;
+    } else {
+        fAA = src.fAA;
+    }
+}
+
+SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
+    fIsBW = true;
+}
+
+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;
+}
+
+bool SkRasterClip::isComplex() const {
+    return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
+}
+
+const SkIRect& SkRasterClip::getBounds() const {
+    return fIsBW ? fBW.getBounds() : fAA.getBounds();
+}
+
+bool SkRasterClip::setEmpty() {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+
+    fIsBW = true;
+    fBW.setEmpty();
+    fAA.setEmpty();
+    return false;
+}
+
+bool SkRasterClip::setRect(const SkIRect& rect) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    
+    fIsBW = true;
+    fAA.setEmpty();
+    return fBW.setRect(rect);
+}
+
+bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+
+    if (this->isBW() && !doAA) {
+        return 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);
+    }
+}
+
+bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
+    SkRegion tmp;
+    tmp.setRect(clip);
+    return this->setPath(path, tmp, doAA);
+}
+
+bool SkRasterClip::setPath(const SkPath& path, const SkRasterClip& clip,
+                           bool doAA) {
+    if (clip.isBW()) {
+        return this->setPath(path, clip.bwRgn(), doAA);
+    } else {
+        SkRegion tmp;
+        tmp.setRect(clip.getBounds());
+        if (!this->setPath(path, clip, doAA)) {
+            return false;
+        }
+        return this->op(clip, SkRegion::kIntersect_Op);
+    }
+}
+
+bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    
+    return fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
+}
+
+bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    
+    if (fIsBW) {
+        return fBW.op(rgn, op);
+    } else {
+        SkAAClip tmp;
+        tmp.setRegion(rgn);
+        return fAA.op(tmp, op);
+    }
+}
+
+bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    clip.validate();
+
+    if (this->isBW() && clip.isBW()) {
+        return fBW.op(clip.fBW, op);
+    } else {
+        SkAAClip tmp;
+        const SkAAClip* other;
+
+        if (this->isBW()) {
+            this->convertToAA();
+        }
+        if (clip.isBW()) {
+            tmp.setRegion(clip.bwRgn());
+            other = &tmp;
+        } else {
+            other = &clip.aaRgn();
+        }
+        return fAA.op(*other, op);
+    }
+}
+
+// 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);
+}
+
+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)) {
+            doAA = false;
+        }
+    }
+
+    if (fIsBW && !doAA) {
+        SkIRect ir;
+        r.round(&ir);
+        return fBW.op(ir, op);
+    } else {
+        if (fIsBW) {
+            this->convertToAA();
+        }
+        return fAA.op(r, op, doAA);
+    }
+}
+
+void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
+    if (NULL == dst) {
+        return;
+    }
+
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    
+    if (this->isEmpty()) {
+        dst->setEmpty();
+        return;
+    }
+    if (0 == (dx | dy)) {
+        *dst = *this;
+        return;
+    }
+
+    dst->fIsBW = fIsBW;
+    if (fIsBW) {
+        fBW.translate(dx, dy, &dst->fBW);
+        dst->fAA.setEmpty();
+    } else {
+        fAA.translate(dx, dy, &dst->fAA);
+        dst->fBW.setEmpty();
+    }
+}
+
+bool SkRasterClip::quickContains(const SkIRect& ir) const {
+    return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkRegion& SkRasterClip::forceGetBW() {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    
+    if (!fIsBW) {
+        fBW.setRect(fAA.getBounds());
+    }
+    return fBW;
+}
+
+void SkRasterClip::convertToAA() {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    
+    SkASSERT(fIsBW);
+    fAA.setRegion(fBW);
+    fIsBW = false;
+}
+
+#ifdef SK_DEBUG
+void SkRasterClip::validate() const {
+    // can't ever assert that fBW is empty, since we may have called forceGetBW
+    if (fIsBW) {
+        SkASSERT(fAA.isEmpty());
+    }
+
+    fBW.validate();
+    fAA.validate();
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
+    SkDEBUGCODE(fClipRgn = NULL;)
+    SkDEBUGCODE(fBlitter = NULL;)
+}
+
+SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
+                                               SkBlitter* blitter) {
+    this->init(clip, blitter);
+}
+
+SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
+                                               SkBlitter* blitter) {
+    SkASSERT(blitter);
+    SkASSERT(aaclip);
+    fBWRgn.setRect(aaclip->getBounds());
+    fAABlitter.init(blitter, aaclip);
+    // now our return values
+    fClipRgn = &fBWRgn;
+    fBlitter = &fAABlitter;
+}
+
+void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
+    SkASSERT(blitter);
+    if (clip.isBW()) {
+        fClipRgn = &clip.bwRgn();
+        fBlitter = blitter;
+    } else {
+        const SkAAClip& aaclip = clip.aaRgn();
+        fBWRgn.setRect(aaclip.getBounds());
+        fAABlitter.init(blitter, &aaclip);
+        // now our return values
+        fClipRgn = &fBWRgn;
+        fBlitter = &fAABlitter;
+    }
+}
+
diff --git a/legacy/src/core/SkRasterClip.h b/legacy/src/core/SkRasterClip.h
new file mode 100644
index 0000000..8f18270
--- /dev/null
+++ b/legacy/src/core/SkRasterClip.h
@@ -0,0 +1,139 @@
+/*
+ * 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 SkRasterClip_DEFINED
+#define SkRasterClip_DEFINED
+
+#include "SkRegion.h"
+#include "SkAAClip.h"
+
+class SkRasterClip {
+public:
+    SkRasterClip();
+    SkRasterClip(const SkIRect&);
+    SkRasterClip(const SkRasterClip&);
+    ~SkRasterClip();
+
+    bool isBW() const { return fIsBW; }
+    bool isAA() const { return !fIsBW; }
+    const SkRegion& bwRgn() const { SkASSERT(fIsBW); return fBW; }
+    const SkAAClip& aaRgn() const { SkASSERT(!fIsBW); return fAA; }
+
+    bool isEmpty() const;
+    bool isRect() const;
+    bool isComplex() const;
+    const SkIRect& getBounds() const;
+
+    bool setEmpty();
+    bool setRect(const SkIRect&);
+
+    bool setPath(const SkPath& path, const SkRegion& clip, bool doAA);
+    bool setPath(const SkPath& path, const SkIRect& clip, bool doAA);
+    bool setPath(const SkPath& path, const SkRasterClip&, bool doAA);
+
+    bool op(const SkIRect&, SkRegion::Op);
+    bool op(const SkRegion&, SkRegion::Op);
+    bool op(const SkRasterClip&, SkRegion::Op);
+    bool op(const SkRect&, SkRegion::Op, bool doAA);
+
+    void translate(int dx, int dy, SkRasterClip* dst) const;
+    void translate(int dx, int dy) {
+        this->translate(dx, dy, this);
+    }
+
+    bool quickContains(const SkIRect& rect) const;
+    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
+     *  intersect, but returning true is a guarantee that they do not.
+     */
+    bool quickReject(const SkIRect& rect) const {
+        return this->isEmpty() || rect.isEmpty() ||
+               !SkIRect::Intersects(this->getBounds(), rect);
+    }
+    
+    // hack for SkCanvas::getTotalClip
+    const SkRegion& forceGetBW();
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    SkRegion    fBW;
+    SkAAClip    fAA;
+    bool        fIsBW;
+
+    void convertToAA();
+};
+
+class SkAutoRasterClipValidate : SkNoncopyable {
+public:
+    SkAutoRasterClipValidate(const SkRasterClip& rc) : fRC(rc) {
+        fRC.validate();
+    }
+    ~SkAutoRasterClipValidate() {
+        fRC.validate();
+    }
+private:
+    const SkRasterClip& fRC;
+};
+
+#ifdef SK_DEBUG
+    #define AUTO_RASTERCLIP_VALIDATE(rc)    SkAutoRasterClipValidate arcv(rc)
+#else
+    #define AUTO_RASTERCLIP_VALIDATE(rc)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Encapsulates the logic of deciding if we need to change/wrap the blitter
+ *  for aaclipping. If so, getRgn and getBlitter return modified values. If
+ *  not, they return the raw blitter and (bw) clip region.
+ *
+ *  We need to keep the constructor/destructor cost as small as possible, so we
+ *  can freely put this guy on the stack, and not pay too much for the case when
+ *  we're really BW anyways.
+ */
+class SkAAClipBlitterWrapper {
+public:
+    SkAAClipBlitterWrapper();
+    SkAAClipBlitterWrapper(const SkRasterClip&, SkBlitter*);
+    SkAAClipBlitterWrapper(const SkAAClip*, SkBlitter*);
+    
+    void init(const SkRasterClip&, SkBlitter*);
+
+    const SkIRect& getBounds() const {
+        SkASSERT(fClipRgn);
+        return fClipRgn->getBounds();
+    }
+    const SkRegion& getRgn() const {
+        SkASSERT(fClipRgn);
+        return *fClipRgn;
+    }
+    SkBlitter* getBlitter() {
+        SkASSERT(fBlitter);
+        return fBlitter;
+    }
+    
+private:
+    const SkAAClip* fAAClip;
+    SkRegion        fBWRgn;
+    SkAAClipBlitter fAABlitter;
+    // what we return
+    const SkRegion* fClipRgn;
+    SkBlitter* fBlitter;
+};
+
+#endif
diff --git a/legacy/src/core/SkRasterizer.cpp b/legacy/src/core/SkRasterizer.cpp
new file mode 100644
index 0000000..7ccced8
--- /dev/null
+++ b/legacy/src/core/SkRasterizer.cpp
@@ -0,0 +1,51 @@
+
+/*
+ * 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 "SkRasterizer.h"
+#include "SkDraw.h"
+#include "SkMaskFilter.h"
+#include "SkPath.h"
+
+// do nothing for now, since we don't store anything at flatten time
+SkRasterizer::SkRasterizer(SkFlattenableReadBuffer&) {}
+
+bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix,
+                             const SkIRect* clipBounds, SkMaskFilter* filter,
+                             SkMask* mask, SkMask::CreateMode mode) {
+    SkIRect storage;
+    
+    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;
+        if (!filter->filterMask(&dstM, srcM, matrix, &margin)) {
+            return false;
+        }
+        storage = *clipBounds;
+        storage.inset(-margin.fX, -margin.fY);
+        clipBounds = &storage;
+    }
+    
+    return this->onRasterize(fillPath, matrix, clipBounds, mask, mode);
+}
+
+/*  Our default implementation of the virtual method just scan converts
+*/
+bool SkRasterizer::onRasterize(const SkPath& fillPath, const SkMatrix& matrix,
+                             const SkIRect* clipBounds,
+                             SkMask* mask, SkMask::CreateMode mode) {
+    SkPath  devPath;
+    
+    fillPath.transform(matrix, &devPath);
+    return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode);
+}
+
diff --git a/legacy/src/core/SkRect.cpp b/legacy/src/core/SkRect.cpp
new file mode 100644
index 0000000..5e3d93c
--- /dev/null
+++ b/legacy/src/core/SkRect.cpp
@@ -0,0 +1,165 @@
+
+/*
+ * 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 "SkRect.h"
+
+void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom) {
+    // do nothing if the params are empty
+    if (left >= right || top >= bottom) {
+        return;
+    }
+
+    // if we are empty, just assign
+    if (fLeft >= fRight || fTop >= fBottom) {
+        this->set(left, top, right, bottom);
+    } else {
+        if (left < fLeft) fLeft = left;
+        if (top < fTop) fTop = top;
+        if (right > fRight) fRight = right;
+        if (bottom > fBottom) fBottom = bottom;
+    }
+}
+
+void SkIRect::sort() {
+    if (fLeft > fRight) {
+        SkTSwap<int32_t>(fLeft, fRight);
+    }
+    if (fTop > fBottom) {
+        SkTSwap<int32_t>(fTop, fBottom);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void SkRect::sort() {
+    if (fLeft > fRight) {
+        SkTSwap<SkScalar>(fLeft, fRight);
+    }
+    if (fTop > fBottom) {
+        SkTSwap<SkScalar>(fTop, fBottom);
+    }
+}
+
+void SkRect::toQuad(SkPoint quad[4]) const {
+    SkASSERT(quad);
+
+    quad[0].set(fLeft, fTop);
+    quad[1].set(fRight, fTop);
+    quad[2].set(fRight, fBottom);
+    quad[3].set(fLeft, fBottom);
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkFLOATCODE(code)   code
+#else
+    #define SkFLOATCODE(code)
+#endif
+
+void SkRect::set(const SkPoint pts[], int count) {
+    SkASSERT((pts && count > 0) || count == 0);
+
+    if (count <= 0) {
+        sk_bzero(this, sizeof(SkRect));
+    } else {
+#ifdef SK_SCALAR_SLOW_COMPARES
+        int32_t    l, t, r, b;
+
+        l = r = SkScalarAs2sCompliment(pts[0].fX);
+        t = b = SkScalarAs2sCompliment(pts[0].fY);
+
+        for (int i = 1; i < count; i++) {
+            int32_t x = SkScalarAs2sCompliment(pts[i].fX);
+            int32_t y = SkScalarAs2sCompliment(pts[i].fY);
+
+            if (x < l) l = x; else if (x > r) r = x;
+            if (y < t) t = y; else if (y > b) b = y;
+        }
+        this->set(Sk2sComplimentAsScalar(l),
+                  Sk2sComplimentAsScalar(t),
+                  Sk2sComplimentAsScalar(r),
+                  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);)
+
+        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;
+        }
+
+#ifdef SK_SCALAR_IS_FLOAT
+        if (isNaN) {
+            l = t = r = b = 0;
+        }
+#endif
+        this->set(l, t, r, b);
+#endif
+    }
+}
+
+bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right,
+                       SkScalar bottom) {
+    if (left < right && top < bottom && !this->isEmpty() && // check for empties
+        fLeft < right && left < fRight && fTop < bottom && top < fBottom)
+    {
+        if (fLeft < left) fLeft = left;
+        if (fTop < top) fTop = top;
+        if (fRight > right) fRight = right;
+        if (fBottom > bottom) fBottom = bottom;
+        return true;
+    }
+    return false;
+}
+
+bool SkRect::intersect(const SkRect& r) {
+    SkASSERT(&r);
+    return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+}
+
+bool SkRect::intersect(const SkRect& a, const SkRect& 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) {
+        fLeft   = SkMaxScalar(a.fLeft,   b.fLeft);
+        fTop    = SkMaxScalar(a.fTop,    b.fTop);
+        fRight  = SkMinScalar(a.fRight,  b.fRight);
+        fBottom = SkMinScalar(a.fBottom, b.fBottom);
+        return true;
+    }
+    return false;
+}
+
+void SkRect::join(SkScalar left, SkScalar top, SkScalar right,
+                  SkScalar bottom) {
+    // do nothing if the params are empty
+    if (left >= right || top >= bottom) {
+        return;
+    }
+
+    // if we are empty, just assign
+    if (fLeft >= fRight || fTop >= fBottom) {
+        this->set(left, top, right, bottom);
+    } else {
+        if (left < fLeft) fLeft = left;
+        if (top < fTop) fTop = top;
+        if (right > fRight) fRight = right;
+        if (bottom > fBottom) fBottom = bottom;
+    }
+}
+
diff --git a/legacy/src/core/SkRefDict.cpp b/legacy/src/core/SkRefDict.cpp
new file mode 100644
index 0000000..53b099b
--- /dev/null
+++ b/legacy/src/core/SkRefDict.cpp
@@ -0,0 +1,89 @@
+
+/*
+ * 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 "SkRefDict.h"
+#include "SkString.h"
+
+struct SkRefDict::Impl {
+    Impl*       fNext;
+    SkString    fName;
+    SkRefCnt*   fData;
+};
+
+SkRefDict::SkRefDict() : fImpl(NULL) {}
+
+SkRefDict::~SkRefDict() {
+    this->removeAll();
+}
+
+SkRefCnt* SkRefDict::find(const char name[]) const {
+    if (NULL == name) {
+        return NULL;
+    }
+
+    Impl* rec = fImpl;
+    while (rec) {
+        if (rec->fName.equals(name)) {
+            return rec->fData;
+        }
+        rec = rec->fNext;
+    }
+    return NULL;
+}
+
+void SkRefDict::set(const char name[], SkRefCnt* data) {
+    if (NULL == name) {
+        return;
+    }
+
+    Impl* rec = fImpl;
+    Impl* prev = NULL;
+    while (rec) {
+        if (rec->fName.equals(name)) {
+            if (data) {
+                // replace
+                data->ref();
+                rec->fData->unref();
+                rec->fData = data;
+            } else {
+                // remove
+                rec->fData->unref();
+                if (prev) {
+                    prev->fNext = rec->fNext;
+                } else {
+                    fImpl = rec->fNext;
+                }
+            }
+            return;
+        }
+        prev = rec;
+        rec = rec->fNext;
+    }
+
+    // if get here, name was not found, so add it
+    data->ref();
+    rec = new Impl;
+    rec->fName.set(name);
+    rec->fData = data;
+    // prepend to the head of our list
+    rec->fNext = fImpl;
+    fImpl = rec;
+}
+
+void SkRefDict::removeAll() {
+    Impl* rec = fImpl;
+    while (rec) {
+        Impl* next = rec->fNext;
+        rec->fData->unref();
+        delete rec;
+        rec = next;
+    }
+    fImpl = NULL;
+}
+
diff --git a/legacy/src/core/SkRegion.cpp b/legacy/src/core/SkRegion.cpp
new file mode 100644
index 0000000..dee652b
--- /dev/null
+++ b/legacy/src/core/SkRegion.cpp
@@ -0,0 +1,1352 @@
+
+/*
+ * 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 "SkRegionPriv.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+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;
+    }
+    return (SkRegion::RunType*)(runs + 1);  // return past the X-sentinel
+}
+
+// returns true if runs are just a rect
+bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count, SkIRect* bounds)
+{
+    assert_sentinel(runs[0], false);    // top
+
+    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);
+        assert_sentinel(runs[5], true);
+
+        SkASSERT(runs[0] < runs[1]);    // valid height
+        SkASSERT(runs[2] < runs[3]);    // valid width
+
+        bounds->set(runs[2], runs[0], runs[3], 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;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+SkRegion::SkRegion() {
+    fBounds.set(0, 0, 0, 0);
+    fRunHead = SkRegion_gEmptyRunHeadPtr;
+}
+
+SkRegion::SkRegion(const SkRegion& src) {
+    fRunHead = SkRegion_gEmptyRunHeadPtr;   // just need a value that won't trigger sk_free(fRunHead)
+    this->setRegion(src);
+}
+
+SkRegion::SkRegion(const SkIRect& rect) {
+    fRunHead = SkRegion_gEmptyRunHeadPtr;   // just need a value that won't trigger sk_free(fRunHead)
+    this->setRect(rect);
+}
+
+SkRegion::~SkRegion() {
+    this->freeRuns();
+}
+
+void SkRegion::freeRuns() {
+    if (fRunHead->isComplex()) {
+        SkASSERT(fRunHead->fRefCnt >= 1);
+        if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) {
+            //SkASSERT(gRgnAllocCounter > 0);
+            //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
+            //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
+            sk_free(fRunHead);
+        }
+    }
+}
+
+void SkRegion::allocateRuns(int count) {
+    fRunHead = RunHead::Alloc(count);
+}
+
+SkRegion& SkRegion::operator=(const SkRegion& src) {
+    (void)this->setRegion(src);
+    return *this;
+}
+
+void SkRegion::swap(SkRegion& other) {
+    SkTSwap<SkIRect>(fBounds, other.fBounds);
+    SkTSwap<RunHead*>(fRunHead, other.fRunHead);
+}
+
+bool SkRegion::setEmpty() {
+    this->freeRuns();
+    fBounds.set(0, 0, 0, 0);
+    fRunHead = SkRegion_gEmptyRunHeadPtr;
+    return false;
+}
+
+bool SkRegion::setRect(int32_t left, int32_t top,
+                       int32_t right, int32_t bottom) {
+    if (left >= right || top >= bottom) {
+        return this->setEmpty();
+    }
+    this->freeRuns();
+    fBounds.set(left, top, right, bottom);
+    fRunHead = SkRegion_gRectRunHeadPtr;
+    return true;
+}
+
+bool SkRegion::setRect(const SkIRect& r) {
+    return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+}
+
+bool SkRegion::setRegion(const SkRegion& src) {
+    if (this != &src) {
+        this->freeRuns();
+
+        fBounds = src.fBounds;
+        fRunHead = src.fRunHead;
+        if (fRunHead->isComplex()) {
+            sk_atomic_inc(&fRunHead->fRefCnt);
+        }
+    }
+    return fRunHead != SkRegion_gEmptyRunHeadPtr;
+}
+
+bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op) {
+    SkRegion tmp(rect);
+
+    return this->op(tmp, rgn, op);
+}
+
+bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op) {
+    SkRegion tmp(rect);
+
+    return this->op(rgn, tmp, op);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_BUILD_FOR_ANDROID
+char* SkRegion::toString()
+{
+    Iterator iter(*this);
+    int count = 0;
+    while (!iter.done()) {
+        count++;
+        iter.next();
+    }
+    // 4 ints, up to 10 digits each plus sign, 3 commas, '(', ')', SkRegion() and '\0'
+    const int max = (count*((11*4)+5))+11+1;
+    char* result = (char*)malloc(max);
+    if (result == NULL) {
+        return NULL;
+    }
+    count = sprintf(result, "SkRegion(");
+    iter.reset(*this);
+    while (!iter.done()) {
+        const SkIRect& r = iter.rect();
+        count += sprintf(result+count, "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
+        iter.next();
+    }
+    count += sprintf(result+count, ")");
+    return result;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+int SkRegion::count_runtype_values(int* itop, int* ibot) const
+{
+    if (this == NULL)
+    {
+        *itop = SK_MinS32;
+        *ibot = SK_MaxS32;
+        return 0;
+    }
+
+    int maxT;
+
+    if (this->isRect())
+        maxT = 2;
+    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);
+    }
+    *itop = fBounds.fTop;
+    *ibot = fBounds.fBottom;
+    return maxT;
+}
+
+bool SkRegion::setRuns(RunType runs[], int count)
+{
+    SkDEBUGCODE(this->validate();)
+    SkASSERT(count > 0);
+
+    if (count <= 2)
+    {
+    //  SkDEBUGF(("setRuns: empty\n"));
+        assert_sentinel(runs[count-1], true);
+        return this->setEmpty();
+    }
+
+    // trim off any empty spans from the top and bottom
+    // weird I should need this, perhaps op() could be smarter...
+    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
+            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
+        }
+
+        // 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);
+        }
+        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));
+        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
+        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));
+
+    SkDEBUGCODE(this->validate();)
+
+    return true;
+}
+
+void SkRegion::BuildRectRuns(const SkIRect& bounds,
+                             RunType runs[kRectRegionRuns])
+{
+    runs[0] = bounds.fTop;
+    runs[1] = bounds.fBottom;
+    runs[2] = bounds.fLeft;
+    runs[3] = bounds.fRight;
+    runs[4] = kRunTypeSentinel;
+    runs[5] = 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
+
+    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))
+        return false;
+
+    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;
+        }
+    }
+    return false;
+}
+
+bool SkRegion::contains(const SkIRect& r) const
+{
+    SkRegion tmp(r);
+    
+    return this->contains(tmp);
+}
+
+bool SkRegion::contains(const SkRegion& rgn) const
+{
+    if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds))
+        return false;
+
+    if (this->isRect())
+        return true;
+
+    SkRegion    tmp;
+    
+    tmp.op(*this, rgn, kUnion_Op);
+    return tmp == *this;
+}
+
+const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[], int* count) const
+{
+    SkASSERT(tmpStorage && count);
+    const RunType* runs = tmpStorage;
+
+    if (this->isEmpty())
+    {
+        tmpStorage[0] = kRunTypeSentinel;
+        *count = 1;
+    }
+    else if (this->isRect())
+    {
+        BuildRectRuns(fBounds, tmpStorage);
+        *count = kRectRegionRuns;
+    }
+    else
+    {
+        *count = fRunHead->fRunCount;
+        runs = fRunHead->readonly_runs();
+    }
+    return runs;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkRegion::intersects(const SkIRect& r) const {
+    if (this->isEmpty() || r.isEmpty()) {
+        return false;
+    }
+    
+    if (!SkIRect::Intersects(fBounds, r)) {
+        return false;
+    }
+
+    if (this->isRect()) {
+        return true;
+    }
+    
+    // we are complex
+    SkRegion tmp;
+    return tmp.op(*this, r, kIntersect_Op);
+}
+
+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()) {
+        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);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkRegion::operator==(const SkRegion& b) const {
+    SkDEBUGCODE(validate();)
+    SkDEBUGCODE(b.validate();)
+
+    if (this == &b) {
+        return true;
+    }
+    if (fBounds != b.fBounds) {
+        return false;
+    }
+    
+    const SkRegion::RunHead* ah = fRunHead;
+    const SkRegion::RunHead* bh = b.fRunHead;
+
+    // this catches empties and rects being equal
+    if (ah == bh) {
+        return true;
+    }
+    // now we insist that both are complex (but different ptrs)
+    if (!ah->isComplex() || !bh->isComplex()) {
+        return false;
+    }
+    return  ah->fRunCount == bh->fRunCount &&
+            !memcmp(ah->readonly_runs(), bh->readonly_runs(),
+                    ah->fRunCount * sizeof(SkRegion::RunType));
+}
+
+void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (NULL == dst) {
+        return;
+    }
+    if (this->isEmpty()) {
+        dst->setEmpty();
+    } else if (this->isRect()) {
+        dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
+                     fBounds.fRight + dx, fBounds.fBottom + dy);
+    } else {
+        if (this == dst) {
+            dst->fRunHead = dst->fRunHead->ensureWritable();
+        } else {
+            SkRegion    tmp;
+            tmp.allocateRuns(fRunHead->fRunCount);
+            tmp.fBounds = fBounds;
+            dst->swap(tmp);
+        }
+
+        dst->fBounds.offset(dx, dy);
+        
+        const RunType*  sruns = fRunHead->readonly_runs();
+        RunType*        druns = dst->fRunHead->writable_runs();
+
+        *druns++ = (SkRegion::RunType)(*sruns++ + dy);    // top
+        for (;;) {
+            int bottom = *sruns++;
+            if (bottom == kRunTypeSentinel) {
+                break;
+            }
+            *druns++ = (SkRegion::RunType)(bottom + dy);  // bottom;
+            for (;;) {
+                int x = *sruns++;
+                if (x == kRunTypeSentinel) {
+                    break;
+                }
+                *druns++ = (SkRegion::RunType)(x + dx);
+                *druns++ = (SkRegion::RunType)(*sruns++ + dx);
+            }
+            *druns++ = kRunTypeSentinel;    // x sentinel
+        }
+        *druns++ = kRunTypeSentinel;    // y sentinel
+
+        SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
+        SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
+    }
+
+    SkDEBUGCODE(this->validate();)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkRegion::setRects(const SkIRect rects[], int count) {
+    if (0 == count) {
+        this->setEmpty();
+    } else {
+        this->setRect(rects[0]);
+        for (int i = 1; i < count; i++) {
+            this->op(rects[i], kUnion_Op);
+        }
+    }
+    return !this->isEmpty();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : local variable used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+#ifdef SK_DEBUG
+static void assert_valid_pair(int left, int rite)
+{
+    SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
+}
+#else
+    #define assert_valid_pair(left, rite)
+#endif
+
+struct spanRec {
+    const SkRegion::RunType*    fA_runs;
+    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[])
+    {        
+        fA_left = *a_runs++;
+        fA_rite = *a_runs++;
+        fB_left = *b_runs++;
+        fB_rite = *b_runs++;
+
+        fA_runs = a_runs;
+        fB_runs = b_runs;
+    }
+    
+    bool done() const
+    {
+        SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
+        SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
+        return fA_left == SkRegion::kRunTypeSentinel && fB_left == SkRegion::kRunTypeSentinel;
+    }
+
+    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)
+        {
+            inside = 1;
+            left = a_left;
+            if (a_rite <= b_left)   // [...] <...>
+            {
+                rite = a_rite;
+                a_flush = true;
+            }
+            else // [...<..]...> or [...<...>...]
+                rite = a_left = b_left;
+        }
+        else if (b_left < a_left)
+        {
+            inside = 2;
+            left = b_left;
+            if (b_rite <= a_left)   // [...] <...>
+            {
+                rite = b_rite;
+                b_flush = true;
+            }
+            else // [...<..]...> or [...<...>...]
+                rite = b_left = a_left;
+        }
+        else    // a_left == b_left
+        {
+            inside = 3;
+            left = a_left;  // or b_left
+            if (a_rite <= b_rite)
+            {
+                rite = b_left = a_rite;
+                a_flush = true;
+            }
+            if (b_rite <= a_rite)
+            {
+                rite = a_left = b_rite;
+                b_flush = true;
+            }
+        }
+
+        if (a_flush)
+        {
+            a_left = *fA_runs++;
+            a_rite = *fA_runs++;
+        }
+        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;
+    }
+};
+
+static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
+                                          const SkRegion::RunType b_runs[],
+                                          SkRegion::RunType dst[],
+                                          int min, int max)
+{
+    spanRec rec;
+    bool    firstInterval = true;
+    
+    rec.init(a_runs, b_runs);
+
+    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)
+            {
+                *dst++ = (SkRegion::RunType)(left);
+                *dst++ = (SkRegion::RunType)(rite);
+                firstInterval = false;
+            }
+            else    // update the right edge
+                dst[-1] = (SkRegion::RunType)(rite);
+        }
+    }
+
+    *dst++ = SkRegion::kRunTypeSentinel;
+    return dst;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+static const struct {
+    uint8_t fMin;
+    uint8_t fMax;
+} gOpMinMax[] = {
+    { 1, 1 },   // Difference
+    { 3, 3 },   // Intersection
+    { 1, 3 },   // Union
+    { 1, 2 }    // XOR
+};
+
+class RgnOper {
+public:
+    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);
+        SkASSERT(SkRegion::kUnion_Op == 2);
+        SkASSERT(SkRegion::kXOR_Op == 3);
+        SkASSERT((unsigned)op <= 3);
+
+        fStartDst = dst;
+        fPrevDst = dst + 1;
+        fPrevLen = 0;       // will never match a length from operate_on_span
+        fTop = (SkRegion::RunType)(top);    // just a first guess, we might update this
+
+        fMin = gOpMinMax[op].fMin;
+        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
+        SkRegion::RunType*  stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
+        size_t              len = stop - start;
+
+        if (fPrevLen == len && !memcmp(fPrevDst, start, len * sizeof(SkRegion::RunType)))   // update Y value
+            fPrevDst[-1] = (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);
+                fPrevDst = start;
+                fPrevLen = len;
+            }
+        }
+    }
+    
+    int flush()
+    {
+        fStartDst[0] = fTop;
+        fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
+        return (int)(fPrevDst - fStartDst + fPrevLen + 1);
+    }
+
+    uint8_t fMin, fMax;
+
+private:
+    SkRegion::RunType*  fStartDst;
+    SkRegion::RunType*  fPrevDst;
+    size_t              fPrevLen;
+    SkRegion::RunType   fTop;
+};
+
+static int operate(const SkRegion::RunType a_runs[],
+                   const SkRegion::RunType b_runs[],
+                   SkRegion::RunType dst[],
+                   SkRegion::Op op) {
+    const SkRegion::RunType gSentinel[] = {
+        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,
+    };
+
+    int a_top = *a_runs++;
+    int a_bot = *a_runs++;
+    int b_top = *b_runs++;
+    int b_bot = *b_runs++;
+
+    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;
+        const SkRegion::RunType*    run0 = gSentinel;
+        const SkRegion::RunType*    run1 = gSentinel;
+        bool                        a_flush = false;
+        bool                        b_flush = false;
+
+        if (a_top < b_top) {
+            top = a_top;
+            run0 = a_runs;
+            if (a_bot <= b_top) {   // [...] <...>
+                bot = a_bot;
+                a_flush = true;
+            } else {  // [...<..]...> or [...<...>...]
+                bot = a_top = b_top;
+            }
+        } else if (b_top < a_top) {
+            top = b_top;
+            run1 = b_runs;
+            if (b_bot <= a_top) {   // [...] <...>
+                bot = b_bot;
+                b_flush = true;
+            } else {    // [...<..]...> or [...<...>...]
+                bot = b_top = a_top;
+            }
+        } else {    // a_top == b_top
+            top = a_top;    // or b_top
+            run0 = a_runs;
+            run1 = b_runs;
+            if (a_bot <= b_bot) {
+                bot = b_top = a_bot;
+                a_flush = true;
+            }
+            if (b_bot <= a_bot) {
+                bot = a_top = b_bot;
+                b_flush = true;
+            }
+        }
+        
+        if (top > prevBot) {
+            oper.addSpan(top, gSentinel, gSentinel);
+        }
+        oper.addSpan(bot, run0, run1);
+        firstInterval = false;
+
+        if (a_flush) {
+            a_runs = skip_scanline(a_runs);
+            a_top = a_bot;
+            a_bot = *a_runs++;
+            if (a_bot == SkRegion::kRunTypeSentinel) {
+                a_top = a_bot;
+            }
+        }
+        if (b_flush) {
+            b_runs = skip_scanline(b_runs);
+            b_top = b_bot;
+            b_bot = *b_runs++;
+            if (b_bot == SkRegion::kRunTypeSentinel) {
+                b_top = b_bot;
+            }
+        }
+        
+        prevBot = bot;
+    }
+    return oper.flush();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  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.
+ */
+static int count_to_intervals(int count) {
+    SkASSERT(count >= 6);   // a single rect is 6 values
+    return (count - 4) >> 1;
+}
+
+/*  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
+ */
+static int intervals_to_count(int intervals) {
+    return 1 + intervals * 4 + 1;
+}
+
+/*  Given the counts 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);
+    // 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();)
+
+    SkASSERT((unsigned)op < kOpCount);
+    
+    if (kReplace_Op == op)
+        return this->set(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)
+    {
+        SkTSwap<const SkRegion*>(rgna, rgnb);
+        op = kDifference_Op;
+    }
+
+    SkIRect bounds;
+    bool    a_empty = rgna->isEmpty();
+    bool    b_empty = rgnb->isEmpty();
+    bool    a_rect = rgna->isRect();
+    bool    b_rect = rgnb->isRect();
+
+    switch (op) {
+    case kDifference_Op:
+        if (a_empty)
+            return this->setEmpty();
+        if (b_empty || !SkIRect::Intersects(rgna->fBounds, rgnb->fBounds))
+            return this->setRegion(*rgna);
+        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);
+        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);
+        break;
+
+    case kXOR_Op:
+        if (a_empty)
+            return this->setRegion(*rgnb);
+        if (b_empty)
+            return this->setRegion(*rgna);
+        break;
+    default:
+        SkDEBUGFAIL("unknown region op");
+        return !this->isEmpty();
+    }
+
+    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 dstCount = compute_worst_case_count(a_count, b_count);
+    SkAutoSTMalloc<32, RunType> array(dstCount);
+
+    int count = operate(a_runs, b_runs, array.get(), op);
+    SkASSERT(count <= dstCount);
+    return this->setRuns(array.get(), count);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkBuffer.h"
+
+uint32_t SkRegion::flatten(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 += fRunHead->fRunCount * sizeof(RunType);
+            }
+        }
+        return size;
+    }
+
+    SkWBuffer   buffer(storage);
+
+    if (this->isEmpty()) {
+        buffer.write32(-1);
+    } else {
+        bool isRect = this->isRect();
+
+        buffer.write32(isRect ? 0 : fRunHead->fRunCount);
+        buffer.write(&fBounds, sizeof(fBounds));
+
+        if (!isRect) {
+            buffer.write(fRunHead->readonly_runs(),
+                         fRunHead->fRunCount * sizeof(RunType));
+        }
+    }
+    return buffer.pos();
+}
+
+uint32_t SkRegion::unflatten(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);
+            buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
+        }
+    }
+    this->swap(tmp);
+    return buffer.pos();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkRegion& SkRegion::GetEmptyRegion() {
+    static SkRegion gEmpty;
+    return gEmpty;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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;
+
+    // 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);
+    }
+    return run + 1; // skip sentinel
+}
+
+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
+    {
+        SkASSERT(!fBounds.isEmpty());
+        if (!this->isRect())
+        {
+            SkASSERT(fRunHead->fRefCnt >= 1);
+            SkASSERT(fRunHead->fRunCount >= kRectRegionRuns);
+
+            const RunType* run = fRunHead->readonly_runs();
+            const RunType* stop = run + fRunHead->fRunCount;
+
+            // check that our bounds match our runs
+            {
+                SkIRect bounds;
+                bool isARect = ComputeRunBounds(run, stop - run, &bounds);
+                SkASSERT(!isARect);
+                SkASSERT(bounds == fBounds);
+            }
+
+            SkASSERT(*run == fBounds.fTop);
+            run++;
+            do {
+                run = validate_line(run, fBounds);
+            } while (*run < kRunTypeSentinel);
+            SkASSERT(run + 1 == stop);
+        }
+    }
+}
+
+void SkRegion::dump() const
+{
+    if (this->isEmpty())
+        SkDebugf("  rgn: empty\n");
+    else
+    {
+        SkDebugf("  rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
+        if (this->isComplex())
+        {
+            const RunType* runs = fRunHead->readonly_runs();
+            for (int i = 0; i < fRunHead->fRunCount; i++)
+                SkDebugf(" %d", runs[i]);
+        }
+        SkDebugf("\n");
+    }
+}
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkRegion::Iterator::Iterator(const SkRegion& rgn) {
+    this->reset(rgn);
+}
+
+bool SkRegion::Iterator::rewind() {
+    if (fRgn) {
+        this->reset(*fRgn);
+        return true;
+    }
+    return false;
+}
+
+void SkRegion::Iterator::reset(const SkRegion& rgn) {
+    fRgn = &rgn;
+    if (rgn.isEmpty()) {
+        fDone = true;
+    } else {
+        fDone = false;
+        if (rgn.isRect()) {
+            fRect = rgn.fBounds;
+            fRuns = NULL;
+        } else {
+            fRuns = rgn.fRunHead->readonly_runs();
+            fRect.set(fRuns[2], fRuns[0], fRuns[3], fRuns[1]);
+            fRuns += 4;
+        }
+    }
+}
+
+void SkRegion::Iterator::next() {
+    if (fDone) {
+        return;
+    }
+
+    if (fRuns == NULL) {   // rect case
+        fDone = true;
+        return;
+    }
+
+    const RunType* runs = fRuns;
+
+    if (runs[0] < kRunTypeSentinel) { // valid X value
+        fRect.fLeft = runs[0];
+        fRect.fRight = runs[1];
+        runs += 2;
+    } else {    // we're at the end of a line
+        runs += 1;
+        if (runs[0] < kRunTypeSentinel) { // valid Y value
+            if (runs[1] == kRunTypeSentinel) {    // empty line
+                fRect.fTop = runs[0];
+                runs += 2;
+            } else {
+                fRect.fTop = fRect.fBottom;
+            }
+    
+            fRect.fBottom = runs[0];
+            assert_sentinel(runs[1], false);
+            fRect.fLeft = runs[1];
+            fRect.fRight = runs[2];
+            runs += 3;
+        } else {    // end of rgn
+            fDone = true;
+        }
+    }
+    fRuns = runs;
+}
+
+SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
+        : fIter(rgn), fClip(clip), fDone(true) {
+    const SkIRect& r = fIter.rect();
+
+    while (!fIter.done()) {
+        if (r.fTop >= clip.fBottom) {
+            break;
+        }
+        if (fRect.intersect(clip, r)) {
+            fDone = false;
+            break;
+        }
+        fIter.next();
+    }
+}
+
+void SkRegion::Cliperator::next() {
+    if (fDone) {
+        return;
+    }
+
+    const SkIRect& r = fIter.rect();
+
+    fDone = true;
+    fIter.next();
+    while (!fIter.done()) {
+        if (r.fTop >= fClip.fBottom) {
+            break;
+        }
+        if (fRect.intersect(fClip, r)) {
+            fDone = false;
+            break;
+        }
+        fIter.next();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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();)
+
+    const SkIRect& r = rgn.getBounds();
+
+    fDone = true;
+    if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
+            right > r.fLeft && left < r.fRight) {
+        if (rgn.isRect()) {
+            if (left < r.fLeft) {
+                left = r.fLeft;
+            }
+            if (right > r.fRight) {
+                right = r.fRight;
+            }
+            fLeft = left;
+            fRight = right;
+            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;
+                    break;
+                }
+            }
+        }
+    }
+}
+
+bool SkRegion::Spanerator::next(int* left, int* right) {
+    if (fDone) {
+        return false;
+    }
+
+    if (fRuns == NULL) {   // we're a rect
+        fDone = true;   // ok, now we're done
+        if (left) {
+            *left = fLeft;
+        }
+        if (right) {
+            *right = fRight;
+        }
+        return true;    // this interval is legal
+    }
+
+    const SkRegion::RunType* runs = fRuns;
+
+    if (runs[0] >= fRight) {
+        fDone = true;
+        return false;
+    }
+
+    SkASSERT(runs[1] > fLeft);
+
+    if (left) {
+        *left = SkMax32(fLeft, runs[0]);
+    }
+    if (right) {
+        *right = SkMin32(fRight, runs[1]);
+    }
+    fRuns = runs + 2;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+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);
+}
+
+#endif
diff --git a/legacy/src/core/SkRegionPriv.h b/legacy/src/core/SkRegionPriv.h
new file mode 100644
index 0000000..648643f
--- /dev/null
+++ b/legacy/src/core/SkRegionPriv.h
@@ -0,0 +1,81 @@
+
+/*
+ * 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 SkRegionPriv_DEFINED
+#define SkRegionPriv_DEFINED
+
+#include "SkRegion.h"
+#include "SkThread.h"
+
+#define assert_sentinel(value, isSentinel) \
+    SkASSERT(((value) == SkRegion::kRunTypeSentinel) == isSentinel)
+
+//SkDEBUGCODE(extern int32_t gRgnAllocCounter;)
+
+struct SkRegion::RunHead {
+    int32_t fRefCnt;
+    int32_t fRunCount;
+    
+    static RunHead* Alloc(int count)
+    {
+        //SkDEBUGCODE(sk_atomic_inc(&gRgnAllocCounter);)
+        //SkDEBUGF(("************** gRgnAllocCounter::alloc %d\n", gRgnAllocCounter));
+
+        SkASSERT(count >= SkRegion::kRectRegionRuns);
+
+        RunHead* head = (RunHead*)sk_malloc_throw(sizeof(RunHead) + count * sizeof(RunType));
+        head->fRefCnt = 1;
+        head->fRunCount = count;
+        return head;
+    }
+    
+    bool isComplex() const
+    {
+        return this != SkRegion_gEmptyRunHeadPtr && this != SkRegion_gRectRunHeadPtr;
+    }
+
+    SkRegion::RunType* writable_runs()
+    {
+        SkASSERT(this->isComplex());
+        SkASSERT(fRefCnt == 1);
+        return (SkRegion::RunType*)(this + 1);
+    }
+    const SkRegion::RunType* readonly_runs() const
+    {
+        SkASSERT(this->isComplex());
+        return (const SkRegion::RunType*)(this + 1);
+    }
+    
+    RunHead* ensureWritable()
+    {
+        SkASSERT(this->isComplex());
+        
+        RunHead* writable = this;
+        if (fRefCnt > 1)
+        {
+            // We need to alloc & copy the current region before we call
+            // sk_atomic_dec because it could be freed in the meantime,
+            // otherwise.            
+            writable = Alloc(fRunCount);
+            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)
+            {
+                sk_free(this);
+            }
+        }
+        return writable;
+    }
+};
+
+#endif
diff --git a/legacy/src/core/SkRegion_path.cpp b/legacy/src/core/SkRegion_path.cpp
new file mode 100644
index 0000000..345ecf8
--- /dev/null
+++ b/legacy/src/core/SkRegion_path.cpp
@@ -0,0 +1,477 @@
+
+/*
+ * 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 "SkRegionPriv.h"
+#include "SkBlitter.h"
+#include "SkScan.h"
+#include "SkTDArray.h"
+#include "SkPath.h"
+
+class SkRgnBuilder : public SkBlitter {
+public:
+    virtual ~SkRgnBuilder();
+    
+    // returns true if it could allocate the working storage needed
+    bool init(int maxHeight, int maxTransitions);
+
+    void done() {
+        if (fCurrScanline != NULL) {
+            fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
+            if (!this->collapsWithPrev()) { // flush the last line
+                fCurrScanline = fCurrScanline->nextScanline();
+            }
+        }
+    }
+
+    int     computeRunCount() const;
+    void    copyToRect(SkIRect*) const;
+    void    copyToRgn(SkRegion::RunType runs[]) const;
+
+    virtual void blitH(int x, int y, int width);
+
+#ifdef SK_DEBUG
+    void dump() const {
+        SkDebugf("SkRgnBuilder: Top = %d\n", fTop);
+        const Scanline* line = (Scanline*)fStorage;
+        while (line < fCurrScanline) {
+            SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount);
+            for (int i = 0; i < line->fXCount; i++) {
+                SkDebugf(" %d", line->firstX()[i]);
+            }
+            SkDebugf("\n");
+
+            line = line->nextScanline();
+        }
+    }
+#endif
+private:
+    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);
+        }
+    };
+    SkRegion::RunType*  fStorage;
+    Scanline*           fCurrScanline;
+    Scanline*           fPrevScanline;
+    //  points at next avialable x[] in fCurrScanline
+    SkRegion::RunType*  fCurrXPtr;
+    SkRegion::RunType   fTop;           // first Y value
+    
+    int fStorageCount;
+
+    bool collapsWithPrev() {
+        if (fPrevScanline != NULL &&
+            fPrevScanline->fLastY + 1 == fCurrScanline->fLastY &&
+            fPrevScanline->fXCount == fCurrScanline->fXCount &&
+            !memcmp(fPrevScanline->firstX(),
+                    fCurrScanline->firstX(),
+                    fCurrScanline->fXCount * sizeof(SkRegion::RunType)))
+        {
+            // update the height of fPrevScanline
+            fPrevScanline->fLastY = fCurrScanline->fLastY;
+            return true;
+        }
+        return false;
+    }
+};
+
+SkRgnBuilder::~SkRgnBuilder() {
+    sk_free(fStorage);
+}
+
+bool SkRgnBuilder::init(int maxHeight, int maxTransitions) {
+    if ((maxHeight | maxTransitions) < 0) {
+        return false;
+    }
+
+    Sk64 count, size;
+
+    // compute the count with +1 and +3 slop for the working buffer
+    count.setMul(maxHeight + 1, 3 + maxTransitions);
+    if (!count.is32() || count.isNeg()) {
+        return false;
+    }
+    fStorageCount = count.get32();
+
+    size.setMul(fStorageCount, sizeof(SkRegion::RunType));
+    if (!size.is32() || size.isNeg()) {
+        return false;
+    }
+
+    fStorage = (SkRegion::RunType*)sk_malloc_flags(size.get32(), 0);
+    if (NULL == fStorage) {
+        return false;
+    }
+
+    fCurrScanline = NULL;    // signal empty collection
+    fPrevScanline = NULL;    // signal first scanline
+    return true;
+}
+
+void SkRgnBuilder::blitH(int x, int y, int width) {
+    if (fCurrScanline == NULL) {  // first time
+        fTop = (SkRegion::RunType)(y);
+        fCurrScanline = (Scanline*)fStorage;
+        fCurrScanline->fLastY = (SkRegion::RunType)(y);
+        fCurrXPtr = fCurrScanline->firstX();
+    } else {
+        SkASSERT(y >= fCurrScanline->fLastY);
+
+        if (y > fCurrScanline->fLastY) {
+            // if we get here, we're done with fCurrScanline
+            fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
+
+            int prevLastY = fCurrScanline->fLastY;
+            if (!this->collapsWithPrev()) {
+                fPrevScanline = fCurrScanline;
+                fCurrScanline = fCurrScanline->nextScanline();
+
+            }
+            if (y - 1 > prevLastY) {  // insert empty run
+                fCurrScanline->fLastY = (SkRegion::RunType)(y - 1);
+                fCurrScanline->fXCount = 0;
+                fCurrScanline = fCurrScanline->nextScanline();
+            }
+            // setup for the new curr line
+            fCurrScanline->fLastY = (SkRegion::RunType)(y);
+            fCurrXPtr = fCurrScanline->firstX();
+        }
+    }
+    //  check if we should extend the current run, or add a new one
+    if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) {
+        fCurrXPtr[-1] = (SkRegion::RunType)(x + width);
+    } else {
+        fCurrXPtr[0] = (SkRegion::RunType)(x);
+        fCurrXPtr[1] = (SkRegion::RunType)(x + width);
+        fCurrXPtr += 2;
+    }
+    SkASSERT(fCurrXPtr - fStorage < fStorageCount);
+}
+
+int SkRgnBuilder::computeRunCount() const {
+    if (fCurrScanline == NULL) {
+        return 0;
+    }
+
+    const SkRegion::RunType*  line = fStorage;
+    const SkRegion::RunType*  stop = (const SkRegion::RunType*)fCurrScanline;
+
+    return 2 + (int)(stop - line);
+}
+
+void SkRgnBuilder::copyToRect(SkIRect* r) const {
+    SkASSERT(fCurrScanline != NULL);
+    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 4);
+
+    const Scanline* line = (const Scanline*)fStorage;
+    SkASSERT(line->fXCount == 2);
+
+    r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1);
+}
+
+void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const {
+    SkASSERT(fCurrScanline != NULL);
+    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4);
+
+    const Scanline* line = (const Scanline*)fStorage;
+    const Scanline* stop = fCurrScanline;
+
+    *runs++ = fTop;
+    do {
+        *runs++ = (SkRegion::RunType)(line->fLastY + 1);
+        int count = line->fXCount;
+        if (count) {
+            memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
+            runs += count;
+        }
+        *runs++ = SkRegion::kRunTypeSentinel;
+        line = line->nextScanline();
+    } while (line < stop);
+    SkASSERT(line == stop);
+    *runs = SkRegion::kRunTypeSentinel;
+}
+
+static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
+    static const uint8_t gPathVerbToInitialLastIndex[] = {
+        0,  //  kMove_Verb
+        1,  //  kLine_Verb
+        2,  //  kQuad_Verb
+        3,  //  kCubic_Verb
+        0,  //  kClose_Verb
+        0   //  kDone_Verb
+    };
+
+    static const uint8_t gPathVerbToMaxEdges[] = {
+        0,  //  kMove_Verb
+        1,  //  kLine_Verb
+        2,  //  kQuad_VerbB
+        3,  //  kCubic_Verb
+        0,  //  kClose_Verb
+        0   //  kDone_Verb
+    };
+
+    SkPath::Iter    iter(path, true);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    int maxEdges = 0;
+    SkScalar    top = SkIntToScalar(SK_MaxS16);
+    SkScalar    bot = SkIntToScalar(SK_MinS16);
+
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        maxEdges += gPathVerbToMaxEdges[verb];
+
+        int lastIndex = gPathVerbToInitialLastIndex[verb];
+        if (lastIndex > 0) {
+            for (int i = 1; i <= lastIndex; i++) {
+                if (top > pts[i].fY) {
+                    top = pts[i].fY;
+                } else if (bot < pts[i].fY) {
+                    bot = pts[i].fY;
+                }
+            }
+        } else if (SkPath::kMove_Verb == verb) {
+            if (top > pts[0].fY) {
+                top = pts[0].fY;
+            } else if (bot < pts[0].fY) {
+                bot = pts[0].fY;
+            }
+        }
+    }
+    SkASSERT(top <= bot);
+
+    *itop = SkScalarRound(top);
+    *ibot = SkScalarRound(bot);
+    return maxEdges;
+}
+
+bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
+    SkDEBUGCODE(this->validate();)
+
+    if (clip.isEmpty()) {
+        return this->setEmpty();
+    }
+
+    if (path.isEmpty()) {
+        if (path.isInverseFillType()) {
+            return this->set(clip);
+        } else {
+            return this->setEmpty();
+        }
+    }
+
+    //  compute worst-case rgn-size for the path
+    int pathTop, pathBot;
+    int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
+    int clipTop, clipBot;
+    int clipTransitions;
+    
+    clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
+
+    int top = SkMax32(pathTop, clipTop);
+    int bot = SkMin32(pathBot, clipBot);
+
+    if (top >= bot)
+        return this->setEmpty();
+
+    SkRgnBuilder builder;
+    
+    if (!builder.init(bot - top, SkMax32(pathTransitions, clipTransitions))) {
+        // can't allocate working space, so return false
+        return this->setEmpty();
+    }
+
+    SkScan::FillPath(path, clip, &builder);
+    builder.done();
+
+    int count = builder.computeRunCount();
+    if (count == 0) {
+        return this->setEmpty();
+    } else if (count == kRectRegionRuns) {
+        builder.copyToRect(&fBounds);
+        this->setRect(fBounds);
+    } else {
+        SkRegion    tmp;
+
+        tmp.fRunHead = RunHead::Alloc(count);
+        builder.copyToRgn(tmp.fRunHead->writable_runs());
+        ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds);
+        this->swap(tmp);
+    }
+    SkDEBUGCODE(this->validate();)
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct Edge {
+    enum {
+        kY0Link = 0x01,
+        kY1Link = 0x02,
+        
+        kCompleteLink = (kY0Link | kY1Link)
+    };
+
+    SkRegion::RunType fX;
+    SkRegion::RunType fY0, fY1;
+    uint8_t fFlags;
+    Edge*   fNext;
+    
+    void set(int x, int y0, int y1) {
+        SkASSERT(y0 != y1);
+
+        fX = (SkRegion::RunType)(x);
+        fY0 = (SkRegion::RunType)(y0);
+        fY1 = (SkRegion::RunType)(y1);
+        fFlags = 0;
+        SkDEBUGCODE(fNext = NULL;)
+    }
+    
+    int top() const {
+        return SkFastMin32(fY0, fY1);
+    }
+};
+
+static void find_link(Edge* base, Edge* stop) {
+    SkASSERT(base < stop);
+
+    if (base->fFlags == Edge::kCompleteLink) {
+        SkASSERT(base->fNext);
+        return;
+    }
+
+    SkASSERT(base + 1 < stop);
+
+    int y0 = base->fY0;
+    int y1 = base->fY1;
+
+    Edge* e = base;
+    if ((base->fFlags & Edge::kY0Link) == 0) {
+        for (;;) {
+            e += 1;
+            if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
+                SkASSERT(NULL == e->fNext);
+                e->fNext = base;
+                e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
+                break;
+            }
+        }
+    }
+    
+    e = base;
+    if ((base->fFlags & Edge::kY1Link) == 0) {
+        for (;;) {
+            e += 1;
+            if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
+                SkASSERT(NULL == base->fNext);
+                base->fNext = e;
+                e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
+                break;
+            }
+        }
+    }
+        
+    base->fFlags = Edge::kCompleteLink;
+}
+
+static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
+    while (0 == edge->fFlags) {
+        edge++; // skip over "used" edges
+    }
+
+    SkASSERT(edge < stop);
+
+    Edge* base = edge;
+    Edge* prev = edge;
+    edge = edge->fNext;
+    SkASSERT(edge != base);
+
+    int count = 1;
+    path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
+    prev->fFlags = 0;
+    do {
+        if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
+            path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1));    // V
+            path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0));    // H
+        }
+        prev = edge;
+        edge = edge->fNext;
+        count += 1;
+        prev->fFlags = 0;
+    } while (edge != base);
+    path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1));    // V
+    path->close();
+    return count;
+}
+
+#include "SkTSearch.h"
+
+static int EdgeProc(const Edge* a, const Edge* b) {
+    return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX;
+}
+
+bool SkRegion::getBoundaryPath(SkPath* path) const {
+    // path could safely be NULL if we're empty, but the caller shouldn't
+    // *know* that
+    SkASSERT(path);
+
+    if (this->isEmpty()) {
+        return false;
+    }
+
+    const SkIRect& bounds = this->getBounds();
+
+    if (this->isRect()) {
+        SkRect  r;        
+        r.set(bounds);      // this converts the ints to scalars
+        path->addRect(r);
+        return true;
+    }
+
+    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);
+    
+    int count = edges.count();
+    Edge* start = edges.begin();
+    Edge* stop = start + count;
+    Edge* e;
+
+    for (e = start; e != stop; e++) {
+        find_link(e, stop);
+    }
+
+#ifdef SK_DEBUG
+    for (e = start; e != stop; e++) {
+        SkASSERT(e->fNext != NULL);
+        SkASSERT(e->fFlags == Edge::kCompleteLink);
+    }
+#endif
+
+    path->incReserve(count << 1);
+    do {
+        SkASSERT(count > 1);
+        count -= extract_path(start, stop, path);
+    } while (count > 0);
+
+    return true;
+}
+
diff --git a/legacy/src/core/SkRegion_rects.cpp b/legacy/src/core/SkRegion_rects.cpp
new file mode 100644
index 0000000..1777a1e
--- /dev/null
+++ b/legacy/src/core/SkRegion_rects.cpp
@@ -0,0 +1,290 @@
+
+/*
+ * 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 "SkRegion.h"
+#include "SkChunkAlloc.h"
+#include "SkTDArray.h"
+#include "SkTemplates.h"
+
+#if 0
+
+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;
+    }
+
+    void backwardsInsert() {
+        while (fPrev->fX > fX) {
+            VEdge* prev = fPrev;
+            VEdge* next = this;
+
+            // 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;
+            next->fNext = prev;
+            prev->fPrev = next;
+        }
+    }
+
+    static void SetFromRect(VEdge edges[], const SkIRect& r) {
+        edges[0].fX = r.fLeft;
+        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;
+        edges[1].fWinding = 1;
+    }
+};
+
+class Accumulator {
+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;
+        SkRegion::RunType   fBottom;
+        int                 fCount; // just [L R] count
+    };
+    SkChunkAlloc    fAlloc;
+    SkTDArray<Row>  fRows;
+    SkRegion::RunType fTop;
+    int             fTotalCount;
+    int             fRectCount;
+};
+
+Accumulator::Accumulator(SkRegion::RunType top, int numRects)
+        : fAlloc((1 + numRects * 2 + 1) * sizeof(int32_t)) {
+    fRectCount = numRects;
+    fTop = top;
+    fTotalCount = 2; // Top + final sentinel
+}
+
+//#define TRACE_ROW(code) code
+#define TRACE_ROW(code)
+
+SkRegion::RunType Accumulator::append(SkRegion::RunType currY, const VEdge* edge) {
+    // worst-case size
+    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;
+
+    // record the L R values for this row
+
+    if (edge->fTop > currY) {
+        nextY = SkMin32(nextY, edge->fTop);
+        TRACE_ROW(SkDebugf("Y %d\n", currY);)
+    } else {
+        SkRegion::RunType currR;
+        *row++ = edge->fX;
+        TRACE_ROW(SkDebugf("Y %d [%d", currY, edge->fX);)
+        edge = edge->fNext;
+        for (;;) {
+            if (edge->fTop > currY) {
+                nextY = SkMin32(nextY, edge->fTop);
+                break;
+            }
+
+            int prevWinding = winding;
+            winding += edge->fWinding;
+            if (0 == winding) { // we finished an interval
+                currR = edge->fX;
+            } else if (0 == prevWinding && edge->fX > currR) {
+                *row++ = currR;
+                *row++ = edge->fX;
+                TRACE_ROW(SkDebugf(" %d] [%d", currR, edge->fX);)
+            }
+            
+            nextY = SkMin32(nextY, edge->fBottom);
+            edge = edge->fNext;
+        }
+        SkASSERT(0 == winding);
+        *row++ = currR;
+        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;
+    if (r && (r->fCount == rowCount) &&
+        !memcmp(r->fPtr, rowHead,
+                rowCount * sizeof(SkRegion::RunType))) {
+            r->fBottom = nextY;    // update bottom
+            fAlloc.unalloc(rowHead);
+        } else {
+            Row* r = fRows.append();
+            r->fPtr = rowHead;
+            r->fBottom = nextY;
+            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) {
+        *dst++ = curr->fBottom;
+        memcpy(dst, curr->fPtr, curr->fCount * sizeof(SkRegion::RunType));
+        dst += curr->fCount;
+        *dst++ = SkRegion::kRunTypeSentinel;
+        curr += 1;
+    }
+    *dst++ = SkRegion::kRunTypeSentinel;
+    SkASSERT(dst - startDst == fTotalCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> int SkTCmp2Int(const T& a, const T& b) {
+    return (a < b) ? -1 : ((b < a) ? 1 : 0);
+}
+
+static inline int SkCmp32(int32_t a, int32_t b) {
+    return (a < b) ? -1 : ((b < a) ? 1 : 0);
+}
+
+static int compare_edgeptr(const void* p0, const void* p1) {
+    const VEdge* e0 = *static_cast<VEdge*const*>(p0);
+    const VEdge* e1 = *static_cast<VEdge*const*>(p1);
+
+    SkRegion::RunType v0 = e0->fTop;
+    SkRegion::RunType v1 = e1->fTop;
+
+    if (v0 == v1) {
+        v0 = e0->fX;
+        v1 = e1->fX;
+    }
+    return SkCmp32(v0, v1);
+}
+
+// fillout edge[] from rects[], sorted. Return the head, and set the tail
+//
+static VEdge* sort_edges(VEdge** edgePtr, VEdge edge[], const SkIRect rects[],
+                         int rectCount, VEdge** edgeTail) {
+    int i;
+    VEdge** ptr = edgePtr;
+    for (int i = 0; i < rectCount; i++) {
+        if (!rects[i].isEmpty()) {
+            VEdge::SetFromRect(edge, rects[i]);
+            *ptr++ = edge++;
+            *ptr++ = edge++;
+        }
+    }
+    
+    int edgeCount = ptr - edgePtr;
+    if (0 == edgeCount) {
+        // all the rects[] were empty
+        return NULL;
+    }
+
+    qsort(edgePtr, edgeCount, sizeof(*edgePtr), compare_edgeptr);
+    for (i = 1; i < edgeCount; i++) {
+        edgePtr[i - 1]->fNext = edgePtr[i];
+        edgePtr[i]->fPrev = edgePtr[i - 1];
+    }
+    *edgeTail = edgePtr[edgeCount - 1];
+    return edgePtr[0];
+}
+
+bool SkRegion::setRects(const SkIRect rects[], int rectCount) {
+    if (0 == rectCount) {
+        return this->setEmpty();
+    }
+    if (1 == rectCount) {
+        return this->setRect(rects[0]);
+    }
+
+    int edgeCount = rectCount * 2;
+    SkAutoMalloc memory((sizeof(VEdge) + sizeof(VEdge*)) * edgeCount);
+    VEdge** edgePtr = (VEdge**)memory.get();
+    VEdge* tail, *head = (VEdge*)(edgePtr + edgeCount);
+    head = sort_edges(edgePtr, head, rects, rectCount, &tail);
+    // check if we have no edges
+    if (NULL == head) {
+        return this->setEmpty();
+    }
+
+    // at this stage, we don't really care about edgeCount, or if rectCount is
+    // larger that it should be (since sort_edges might have skipped some
+    // empty rects[]). rectCount now is just used for worst-case allocations
+
+    VEdge headEdge, tailEdge;
+    headEdge.fPrev = NULL;
+    headEdge.fNext = head;
+    headEdge.fTop = SK_MinS32;
+    headEdge.fX = SK_MinS32;
+    head->fPrev = &headEdge;
+    
+    tailEdge.fPrev = tail;
+    tailEdge.fNext = NULL;
+    tailEdge.fTop = SK_MaxS32;
+    tail->fNext = &tailEdge;
+
+    int32_t currY = head->fTop;
+    Accumulator accum(currY, rectCount);
+    
+    while (head->fNext) {
+        VEdge* edge = head;
+        // accumulate the current
+        SkRegion::RunType nextY = accum.append(currY, edge);
+        // remove the old
+        while (edge->fTop <= currY) {
+            VEdge* next = edge->fNext;
+            if (edge->fBottom <= nextY) {
+                edge->removeFromList();
+            }
+            edge = next;
+        }
+        // insert (sorted) the new
+        while (edge->fTop == nextY) {
+            VEdge* next = edge->fNext;
+            edge->backwardsInsert();
+            edge = next;
+        }
+        currY = nextY;
+        head = headEdge.fNext;
+    }
+
+    SkAutoTArray<RunType> runs(accum.count());
+    accum.copyTo(runs.get());
+    return this->setRuns(runs.get(), accum.count());
+}
+
+#endif
diff --git a/legacy/src/core/SkScalar.cpp b/legacy/src/core/SkScalar.cpp
new file mode 100644
index 0000000..c48d389
--- /dev/null
+++ b/legacy/src/core/SkScalar.cpp
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkMath.h"
+#include "SkScalar.h"
+
+SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[],
+                            const SkScalar values[], int length) {
+    SkASSERT(length > 0);
+    SkASSERT(keys != NULL);
+    SkASSERT(values != NULL);
+#ifdef SK_DEBUG
+    for (int i = 1; i < length; i++)
+        SkASSERT(keys[i] >= keys[i-1]);
+#endif
+    int right = 0;
+    while (right < length && searchKey > keys[right])
+        right++;
+    // Could use sentinel values to eliminate conditionals, but since the
+    // tables are taken as input, a simpler format is better.
+    if (length == right)
+        return values[length-1];
+    if (0 == right)
+        return values[0];
+    // Otherwise, interpolate between right - 1 and right.
+    SkScalar rightKey = keys[right];
+    SkScalar leftKey = keys[right-1];
+    SkScalar fract = SkScalarDiv(searchKey-leftKey,rightKey-leftKey);
+    return SkScalarInterp(values[right-1], values[right], fract);
+}
diff --git a/legacy/src/core/SkScalerContext.cpp b/legacy/src/core/SkScalerContext.cpp
new file mode 100644
index 0000000..eee0dc5
--- /dev/null
+++ b/legacy/src/core/SkScalerContext.cpp
@@ -0,0 +1,782 @@
+
+/*
+ * 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 "SkScalerContext.h"
+#include "SkColorPriv.h"
+#include "SkDescriptor.h"
+#include "SkDraw.h"
+#include "SkFontHost.h"
+#include "SkMaskFilter.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 {
+    SkASSERT(mask);
+
+    mask->fImage = (uint8_t*)fImage;
+    mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight);
+    mask->fRowBytes = this->rowBytes();
+    mask->fFormat = static_cast<SkMask::Format>(fMaskFormat);
+}
+
+size_t SkGlyph::computeImageSize() const {
+    const size_t size = this->rowBytes() * fHeight;
+
+    switch (fMaskFormat) {
+        case SkMask::k3D_Format:
+            return 3 * size;
+        default:
+            return size;
+    }
+}
+
+void SkGlyph::zeroMetrics() {
+    fAdvanceX = 0;
+    fAdvanceY = 0;
+    fWidth    = 0;
+    fHeight   = 0;
+    fTop      = 0;
+    fLeft     = 0;
+    fRsbDelta = 0;
+    fLsbDelta = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+    #define DUMP_RECx
+#endif
+
+static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) {
+    SkFlattenable*  obj = NULL;
+    uint32_t        len;
+    const void*     data = desc->findEntry(tag, &len);
+
+    if (data) {
+        SkFlattenableReadBuffer   buffer(data, len);
+        obj = buffer.readFlattenable();
+        SkASSERT(buffer.offset() == buffer.size());
+    }
+    return obj;
+}
+
+SkScalerContext::SkScalerContext(const SkDescriptor* desc)
+    : fPathEffect(NULL), fMaskFilter(NULL)
+{
+    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",
+             desc->getChecksum(), desc->getCount(), desc->getLength());
+    SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n",
+        rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0],
+        rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]);
+    SkDebugf("  frame %g miter %g hints %d framefill %d format %d join %d\n",
+        rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill,
+        rec->fMaskFormat, rec->fStrokeJoin);
+    SkDebugf("  pathEffect %x maskFilter %x\n",
+             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() {
+    SkDELETE(fNextContext);
+
+    SkSafeUnref(fPathEffect);
+    SkSafeUnref(fMaskFilter);
+    SkSafeUnref(fRasterizer);
+}
+
+static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) {
+    // fonthost will determine the next possible font to search, based
+    // on the current font in fRec. It will return NULL if ctx is our
+    // last font that can be searched (i.e. ultimate fallback font)
+#ifdef SK_BUILD_FOR_ANDROID
+        // On Android, pass entire rec structure so that clients can change fallback behavior
+        uint32_t newFontID = SkFontHost::NextLogicalFont(rec);
+#else
+        uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID);
+#endif
+
+    if (0 == newFontID) {
+        return NULL;
+    }
+
+    SkAutoDescriptor    ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
+    SkDescriptor*       desc = ad.getDesc();
+
+    desc->init();
+    SkScalerContext::Rec* newRec =
+    (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
+                                          sizeof(rec), &rec);
+    newRec->fFontID = newFontID;
+    desc->computeChecksum();
+
+    return SkFontHost::CreateScalerContext(desc);
+}
+
+/*  Return the next context, creating it if its not already created, but return
+    NULL if the fonthost says there are no more fonts to fallback to.
+ */
+SkScalerContext* SkScalerContext::getNextContext() {
+    SkScalerContext* next = fNextContext;
+    // if next is null, then either it isn't cached yet, or we're at the
+    // end of our possible chain
+    if (NULL == next) {
+        next = allocNextContext(fRec);
+        if (NULL == next) {
+            return NULL;
+        }
+        // next's base is our base + our local count
+        next->setBaseGlyphCount(fBaseGlyphCount + this->getGlyphCount());
+        // cache the answer
+        fNextContext = next;
+    }
+    return next;
+}
+
+SkScalerContext* SkScalerContext::getContextFromChar(SkUnichar uni, unsigned& glyphID) {
+    SkScalerContext* ctx = this;
+    for (;;) {
+        glyphID = ctx->generateCharToGlyph(uni);
+        if (glyphID) {
+            break;  // found it
+        }
+        ctx = ctx->getNextContext();
+        if (NULL == ctx) {
+            return NULL;
+        }
+    }
+    return ctx;
+}
+
+SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
+    unsigned glyphID = glyph.getGlyphID();
+    SkScalerContext* ctx = this;
+    for (;;) {
+        unsigned count = ctx->getGlyphCount();
+        if (glyphID < count) {
+            break;
+        }
+        glyphID -= count;
+        ctx = ctx->getNextContext();
+        if (NULL == ctx) {
+            SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
+            // just return the original context (this)
+            return this;
+        }
+    }
+    return ctx;
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+SkFontID SkScalerContext::findTypefaceIdForChar(SkUnichar uni) {
+    unsigned glyphID;
+    SkScalerContext* ctx = getContextFromChar(uni, glyphID);
+    if (ctx) {
+        return ctx->fRec.fFontID;
+    } else {
+        return 0;
+    }
+}
+
+/*  This loops through all available fallback contexts (if needed) until it
+    finds some context that can handle the unichar and return it.
+
+    As this is somewhat expensive operation, it should only be done on the first
+    char of a run.
+ */
+unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) {
+    unsigned glyphID;
+    SkScalerContext* ctx = getContextFromChar(uni, glyphID);
+    if (ctx) {
+        return ctx->fBaseGlyphCount;
+    } else {
+        SkDEBUGF(("--- no context for char %x\n", uni));
+        return this->fBaseGlyphCount;
+    }
+}
+#endif
+
+/*  This loops through all available fallback contexts (if needed) until it
+    finds some context that can handle the unichar. If all fail, returns 0
+ */
+uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
+
+    unsigned glyphID;
+    SkScalerContext* ctx = getContextFromChar(uni, glyphID);
+    if (!ctx) {
+        return 0; // no more contexts, return missing glyph
+    }
+    // add the ctx's base, making glyphID unique for chain of contexts
+    glyphID += ctx->fBaseGlyphCount;
+    // check for overflow of 16bits, since our glyphID cannot exceed that
+    if (glyphID > 0xFFFF) {
+        glyphID = 0;
+    }
+    return SkToU16(glyphID);
+}
+
+SkUnichar SkScalerContext::glyphIDToChar(uint16_t glyphID) {
+    SkScalerContext* ctx = this;
+    unsigned rangeEnd = 0;
+    do {
+        unsigned rangeStart = rangeEnd;
+
+        rangeEnd += ctx->getGlyphCount();
+        if (rangeStart <= glyphID && glyphID < rangeEnd) {
+            return ctx->generateGlyphToChar(glyphID - rangeStart);
+        }
+        ctx = ctx->getNextContext();
+    } while (NULL != ctx);
+    return 0;
+}
+
+void SkScalerContext::getAdvance(SkGlyph* glyph) {
+    // mark us as just having a valid advance
+    glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;
+    // we mark the format before making the call, in case the impl
+    // internally ends up calling its generateMetrics, which is OK
+    // albeit slower than strictly necessary
+    this->getGlyphContext(*glyph)->generateAdvance(glyph);
+}
+
+void SkScalerContext::getMetrics(SkGlyph* glyph) {
+    this->getGlyphContext(*glyph)->generateMetrics(glyph);
+
+    // for now we have separate cache entries for devkerning on and off
+    // in the future we might share caches, but make our measure/draw
+    // code make the distinction. Thus we zap the values if the caller
+    // has not asked for them.
+    if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
+        // no devkern, so zap the fields
+        glyph->fLsbDelta = glyph->fRsbDelta = 0;
+    }
+
+    // if either dimension is empty, zap the image bounds of the glyph
+    if (0 == glyph->fWidth || 0 == glyph->fHeight) {
+        glyph->fWidth   = 0;
+        glyph->fHeight  = 0;
+        glyph->fTop     = 0;
+        glyph->fLeft    = 0;
+        glyph->fMaskFormat = 0;
+        return;
+    }
+
+    if (fGenerateImageFromPath) {
+        SkPath      devPath, fillPath;
+        SkMatrix    fillToDevMatrix;
+
+        this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
+
+        if (fRasterizer) {
+            SkMask  mask;
+
+            if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
+                                       fMaskFilter, &mask,
+                                       SkMask::kJustComputeBounds_CreateMode)) {
+                glyph->fLeft    = mask.fBounds.fLeft;
+                glyph->fTop     = mask.fBounds.fTop;
+                glyph->fWidth   = SkToU16(mask.fBounds.width());
+                glyph->fHeight  = SkToU16(mask.fBounds.height());
+            } else {
+                goto SK_ERROR;
+            }
+        } else {
+            // just use devPath
+            SkIRect ir;
+            devPath.getBounds().roundOut(&ir);
+
+            if (ir.isEmpty() || !ir.is16Bit()) {
+                goto SK_ERROR;
+            }
+            glyph->fLeft    = ir.fLeft;
+            glyph->fTop     = ir.fTop;
+            glyph->fWidth   = SkToU16(ir.width());
+            glyph->fHeight  = SkToU16(ir.height());
+        }
+    }
+
+	if (SkMask::kARGB32_Format != glyph->fMaskFormat) {
+		glyph->fMaskFormat = fRec.fMaskFormat;
+	}
+
+    if (fMaskFilter) {
+        SkMask      src, dst;
+        SkMatrix    matrix;
+
+        glyph->toMask(&src);
+        fRec.getMatrixFrom2x2(&matrix);
+
+        src.fImage = NULL;  // only want the bounds from the filter
+        if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
+            SkASSERT(dst.fImage == NULL);
+            glyph->fLeft    = dst.fBounds.fLeft;
+            glyph->fTop     = dst.fBounds.fTop;
+            glyph->fWidth   = SkToU16(dst.fBounds.width());
+            glyph->fHeight  = SkToU16(dst.fBounds.height());
+            glyph->fMaskFormat = dst.fFormat;
+        }
+    }
+    return;
+
+SK_ERROR:
+    // draw nothing 'cause we failed
+    glyph->fLeft    = 0;
+    glyph->fTop     = 0;
+    glyph->fWidth   = 0;
+    glyph->fHeight  = 0;
+    // put a valid value here, in case it was earlier set to
+    // MASK_FORMAT_JUST_ADVANCE
+    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 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]);
+        }
+        srcP += srcRB;
+        dstP = (uint16_t*)((char*)dstP + dstRB);
+    }
+}
+
+#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) {
+    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);
+        }
+        dstP = (uint16_t*)((char*)dstP + dstRB);
+    }
+}
+
+static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
+    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);
+        }
+        dstP = (SkPMColor*)((char*)dstP + dstRB);
+    }
+}
+
+static void generateMask(const SkMask& mask, const SkPath& path) {
+    SkBitmap::Config config;
+    SkPaint     paint;
+
+    int srcW = mask.fBounds.width();
+    int srcH = mask.fBounds.height();
+    int dstW = srcW;
+    int dstH = srcH;
+    int dstRB = mask.fRowBytes;
+
+    SkMatrix matrix;
+    matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
+                        -SkIntToScalar(mask.fBounds.fTop));
+
+    if (SkMask::kBW_Format == mask.fFormat) {
+        config = SkBitmap::kA1_Config;
+        paint.setAntiAlias(false);
+    } else {
+        config = SkBitmap::kA8_Config;
+        paint.setAntiAlias(true);
+        switch (mask.fFormat) {
+            case SkMask::kA8_Format:
+                break;
+            case SkMask::kLCD16_Format:
+            case SkMask::kLCD32_Format:
+                // TODO: trigger off LCD orientation
+                dstW *= 3;
+                matrix.postScale(SkIntToScalar(3), SK_Scalar1);
+                dstRB = 0;  // signals we need a copy
+                break;
+            default:
+                SkDEBUGFAIL("unexpected mask format");
+        }
+    }
+
+    SkRasterClip clip;
+    clip.setRect(SkIRect::MakeWH(dstW, dstH));
+
+    SkBitmap bm;
+    bm.setConfig(config, dstW, dstH, dstRB);
+
+    if (0 == dstRB) {
+        bm.allocPixels();
+        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");
+        }
+    }
+}
+
+void SkScalerContext::getImage(const SkGlyph& origGlyph) {
+    const SkGlyph*  glyph = &origGlyph;
+    SkGlyph         tmpGlyph;
+
+    if (fMaskFilter) {   // restore the prefilter bounds
+        tmpGlyph.init(origGlyph.fID);
+
+        // need the original bounds, sans our maskfilter
+        SkMaskFilter* mf = fMaskFilter;
+        fMaskFilter = NULL;             // temp disable
+        this->getMetrics(&tmpGlyph);
+        fMaskFilter = mf;               // restore
+
+        tmpGlyph.fImage = origGlyph.fImage;
+
+        // we need the prefilter bounds to be <= filter bounds
+        SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
+        SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
+        glyph = &tmpGlyph;
+    }
+
+    if (fGenerateImageFromPath) {
+        SkPath      devPath, fillPath;
+        SkMatrix    fillToDevMatrix;
+        SkMask      mask;
+
+        this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
+        glyph->toMask(&mask);
+
+        if (fRasterizer) {
+            mask.fFormat = SkMask::kA8_Format;
+            sk_bzero(glyph->fImage, mask.computeImageSize());
+
+            if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
+                                        fMaskFilter, &mask,
+                                        SkMask::kJustRenderImage_CreateMode)) {
+                return;
+            }
+        } else {
+            generateMask(mask, devPath);
+        }
+    } else {
+        this->getGlyphContext(*glyph)->generateImage(*glyph);
+    }
+
+    if (fMaskFilter) {
+        SkMask      srcM, dstM;
+        SkMatrix    matrix;
+
+        // the src glyph image shouldn't be 3D
+        SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
+        glyph->toMask(&srcM);
+        fRec.getMatrixFrom2x2(&matrix);
+
+        if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
+            int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
+            int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
+            int dstRB = origGlyph.rowBytes();
+            int srcRB = dstM.fRowBytes;
+
+            const uint8_t* src = (const uint8_t*)dstM.fImage;
+            uint8_t* dst = (uint8_t*)origGlyph.fImage;
+
+            if (SkMask::k3D_Format == dstM.fFormat) {
+                // we have to copy 3 times as much
+                height *= 3;
+            }
+
+            // clean out our glyph, since it may be larger than dstM
+            //sk_bzero(dst, height * dstRB);
+
+            while (--height >= 0) {
+                memcpy(dst, src, width);
+                src += srcRB;
+                dst += dstRB;
+            }
+            SkMask::FreeImage(dstM.fImage);
+        }
+    }
+}
+
+void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) {
+    this->internalGetPath(glyph, NULL, path, NULL);
+}
+
+void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx,
+                                     SkPaint::FontMetrics* my) {
+    this->generateFontMetrics(mx, my);
+}
+
+SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) {
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
+                                  SkPath* devPath, SkMatrix* fillToDevMatrix) {
+    SkPath  path;
+
+    this->getGlyphContext(glyph)->generatePath(glyph, &path);
+
+    if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
+        SkFixed dx = glyph.getSubXFixed();
+        SkFixed dy = glyph.getSubYFixed();
+        if (dx | dy) {
+            path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy));
+        }
+    }
+
+    if (fRec.fFrameWidth > 0 || fPathEffect != NULL) {
+        // need the path in user-space, with only the point-size applied
+        // so that our stroking and effects will operate the same way they
+        // would if the user had extracted the path themself, and then
+        // called drawPath
+        SkPath      localPath;
+        SkMatrix    matrix, inverse;
+
+        fRec.getMatrixFrom2x2(&matrix);
+        matrix.invert(&inverse);
+        path.transform(inverse, &localPath);
+        // now localPath is only affected by the paint settings, and not the canvas matrix
+
+        SkScalar width = fRec.fFrameWidth;
+
+        if (fPathEffect) {
+            SkPath effectPath;
+
+            if (fPathEffect->filterPath(&effectPath, localPath, &width)) {
+                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);
+        }
+
+        // now return stuff to the caller
+        if (fillToDevMatrix) {
+            *fillToDevMatrix = matrix;
+        }
+        if (devPath) {
+            localPath.transform(matrix, devPath);
+        }
+        if (fillPath) {
+            fillPath->swap(localPath);
+        }
+    } else {   // nothing tricky to do
+        if (fillToDevMatrix) {
+            fillToDevMatrix->reset();
+        }
+        if (devPath) {
+            if (fillPath == NULL) {
+                devPath->swap(path);
+            } else {
+                *devPath = path;
+            }
+        }
+
+        if (fillPath) {
+            fillPath->swap(path);
+        }
+    }
+
+    if (devPath) {
+        devPath->updateBoundsCache();
+    }
+    if (fillPath) {
+        fillPath->updateBoundsCache();
+    }
+}
+
+
+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 SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const {
+    m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
+    if (fPreSkewX) {
+        m->postSkew(fPreSkewX, 0);
+    }
+}
+
+void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const {
+    this->getLocalMatrix(m);
+
+    //  now concat the device matrix
+    SkMatrix    deviceMatrix;
+    this->getMatrixFrom2x2(&deviceMatrix);
+    m->postConcat(deviceMatrix);
+}
+
+SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix) {
+    SkASSERT(!matrix.hasPerspective());
+    
+    if (0 == matrix[SkMatrix::kMSkewY]) {
+        return kX_SkAxisAlignment;
+    }
+    if (0 == matrix[SkMatrix::kMScaleX]) {
+        return kY_SkAxisAlignment;
+    }
+    return kNone_SkAxisAlignment;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkFontHost.h"
+
+class SkScalerContext_Empty : public SkScalerContext {
+public:
+    SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
+
+protected:
+    virtual unsigned generateGlyphCount() {
+        return 0;
+    }
+    virtual uint16_t generateCharToGlyph(SkUnichar uni) {
+        return 0;
+    }
+    virtual void generateAdvance(SkGlyph* glyph) {
+        glyph->zeroMetrics();
+    }
+    virtual void generateMetrics(SkGlyph* glyph) {
+        glyph->zeroMetrics();
+    }
+    virtual void generateImage(const SkGlyph& glyph) {}
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
+                                     SkPaint::FontMetrics* my) {
+        if (mx) {
+            sk_bzero(mx, sizeof(*mx));
+        }
+        if (my) {
+            sk_bzero(my, sizeof(*my));
+        }
+    }
+};
+
+extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc);
+
+SkScalerContext* SkScalerContext::Create(const SkDescriptor* 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/legacy/src/core/SkScan.cpp b/legacy/src/core/SkScan.cpp
new file mode 100644
index 0000000..cee328f
--- /dev/null
+++ b/legacy/src/core/SkScan.cpp
@@ -0,0 +1,118 @@
+
+/*
+ * 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 "SkScan.h"
+#include "SkBlitter.h"
+#include "SkRasterClip.h"
+
+static inline void blitrect(SkBlitter* blitter, const SkIRect& r) {
+    blitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+}
+
+void SkScan::FillIRect(const SkIRect& r, const SkRegion* clip,
+                       SkBlitter* blitter) {
+    if (!r.isEmpty()) {
+        if (clip) {
+            if (clip->isRect()) {
+                const SkIRect& clipBounds = clip->getBounds();
+                
+                if (clipBounds.contains(r)) {
+                    blitrect(blitter, r);
+                } else {
+                    SkIRect rr = r;
+                    if (rr.intersect(clipBounds)) {
+                        blitrect(blitter, rr);
+                    }
+                }
+            } else {
+                SkRegion::Cliperator    cliper(*clip, r);
+                const SkIRect&          rr = cliper.rect();
+                
+                while (!cliper.done()) {
+                    blitrect(blitter, rr);
+                    cliper.next();
+                }
+            }
+        } else {
+            blitrect(blitter, r);
+        }
+    }
+}
+
+void SkScan::FillXRect(const SkXRect& xr, const SkRegion* clip,
+                       SkBlitter* blitter) {
+    SkIRect r;
+    
+    XRect_round(xr, &r);
+    SkScan::FillIRect(r, clip, blitter);
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+void SkScan::FillRect(const SkRect& r, const SkRegion* clip,
+                       SkBlitter* blitter) {
+    SkIRect ir;
+    
+    r.round(&ir);
+    SkScan::FillIRect(ir, clip, blitter);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkScan::FillIRect(const SkIRect& r, const SkRasterClip& clip,
+                       SkBlitter* blitter) {
+    if (clip.isEmpty() || r.isEmpty()) {
+        return;
+    }
+    
+    if (clip.isBW()) {
+        FillIRect(r, &clip.bwRgn(), blitter);
+        return;
+    }
+
+    SkAAClipBlitterWrapper wrapper(clip, blitter);
+    FillIRect(r, &wrapper.getRgn(), wrapper.getBlitter());
+}
+
+void SkScan::FillXRect(const SkXRect& xr, const SkRasterClip& clip,
+                       SkBlitter* blitter) {
+    if (clip.isEmpty() || xr.isEmpty()) {
+        return;
+    }
+    
+    if (clip.isBW()) {
+        FillXRect(xr, &clip.bwRgn(), blitter);
+        return;
+    }
+
+    SkAAClipBlitterWrapper wrapper(clip, blitter);
+    FillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+void SkScan::FillRect(const SkRect& r, const SkRasterClip& clip,
+                      SkBlitter* blitter) {
+    if (clip.isEmpty() || r.isEmpty()) {
+        return;
+    }
+    
+    if (clip.isBW()) {
+        FillRect(r, &clip.bwRgn(), blitter);
+        return;
+    }
+
+    SkAAClipBlitterWrapper wrapper(clip, blitter);
+    FillRect(r, &wrapper.getRgn(), wrapper.getBlitter());
+}
+
+#endif
+
diff --git a/legacy/src/core/SkScanPriv.h b/legacy/src/core/SkScanPriv.h
new file mode 100644
index 0000000..96ed5ab
--- /dev/null
+++ b/legacy/src/core/SkScanPriv.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 SkScanPriv_DEFINED
+#define SkScanPriv_DEFINED
+
+#include "SkScan.h"
+#include "SkBlitter.h"
+
+class SkScanClipper {
+public:
+    SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& bounds);
+
+    SkBlitter*      getBlitter() const { return fBlitter; }
+    const SkIRect*  getClipRect() const { return fClipRect; }
+
+private:
+    SkRectClipBlitter   fRectBlitter;
+    SkRgnClipBlitter    fRgnBlitter;
+    SkBlitter*          fBlitter;
+    const SkIRect*      fClipRect;
+};
+
+// clipRect == null means path is entirely inside the clip
+void sk_fill_path(const SkPath& path, const SkIRect* clipRect,
+                  SkBlitter* blitter, int start_y, int stop_y, int shiftEdgesUp,
+                  const SkRegion& clipRgn);
+
+// blit the rects above and below avoid, clipped to clip
+void sk_blit_above(SkBlitter*, const SkIRect& avoid, const SkRegion& clip);
+void sk_blit_below(SkBlitter*, const SkIRect& avoid, const SkRegion& clip);
+
+#endif
+
diff --git a/legacy/src/core/SkScan_AntiPath.cpp b/legacy/src/core/SkScan_AntiPath.cpp
new file mode 100644
index 0000000..0834caa
--- /dev/null
+++ b/legacy/src/core/SkScan_AntiPath.cpp
@@ -0,0 +1,763 @@
+
+/*
+ * 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 "SkScanPriv.h"
+#include "SkPath.h"
+#include "SkMatrix.h"
+#include "SkBlitter.h"
+#include "SkRegion.h"
+#include "SkAntiRun.h"
+
+#define SHIFT   2
+#define SCALE   (1 << SHIFT)
+#define MASK    (SCALE - 1)
+
+/** @file
+    We have two techniques for capturing the output of the supersampler:
+    - SUPERMASK, which records a large mask-bitmap
+        this is often faster for small, complex objects
+    - RLE, which records a rle-encoded scanline
+        this is often faster for large objects with big spans
+
+    These blitters use two coordinate systems:
+    - destination coordinates, scale equal to the output - often
+        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.
+ */
+//#define FORCE_SUPERMASK
+//#define FORCE_RLE
+//#define SK_SUPPORT_NEW_AA
+//#define SK_USE_EXACT_COVERAGE
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Base class for a single-pass supersampled blitter.
+class BaseSuperBlitter : public SkBlitter {
+public:
+    BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                     const SkRegion& clip);
+
+    /// Must be explicitly defined on subclasses.
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
+                           const int16_t runs[]) SK_OVERRIDE {
+        SkDEBUGFAIL("How did I get here?");
+    }
+    /// May not be called on BaseSuperBlitter because it blits out of order.
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
+        SkDEBUGFAIL("How did I get here?");
+    }
+
+protected:
+    SkBlitter*  fRealBlitter;
+    /// Current y coordinate, in destination coordinates.
+    int         fCurrIY;
+    /// Widest row of region to be blitted, in destination coordinates.
+    int         fWidth;
+    /// Leftmost x coordinate in any row, in destination coordinates.
+    int         fLeft;
+    /// Leftmost x coordinate in any row, in supersampled coordinates.
+    int         fSuperLeft;
+
+    SkDEBUGCODE(int fCurrX;)
+    /// Current y coordinate in supersampled coordinates.
+    int fCurrY;
+    /// Initial y coordinate (top of bounds).
+    int fTop;
+};
+
+BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                                   const SkRegion& clip) {
+    fRealBlitter = realBlitter;
+
+    /*
+     *  We use the clip bounds instead of the ir, since we may be asked to
+     *  draw outside of the rect if we're a inverse filltype
+     */
+    const int left = clip.getBounds().fLeft;
+    const int right = clip.getBounds().fRight;
+    
+    fLeft = left;
+    fSuperLeft = left << SHIFT;
+    fWidth = right - left;
+#if 0
+    fCurrIY = -1;
+    fCurrY = -1;
+#else
+    fTop = ir.fTop;
+    fCurrIY = ir.fTop - 1;
+    fCurrY = (ir.fTop << SHIFT) - 1;
+#endif
+    SkDEBUGCODE(fCurrX = -1;)
+}
+
+/// Run-length-encoded supersampling antialiased blitter.
+class SuperBlitter : public BaseSuperBlitter {
+public:
+    SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                 const SkRegion& clip);
+
+    virtual ~SuperBlitter() {
+        this->flush();
+        sk_free(fRuns.fRuns);
+    }
+
+    /// Once fRuns contains a complete supersampled row, flush() blits
+    /// it out through the wrapped blitter.
+    void flush();
+
+    /// Blits a row of pixels, with location and width specified
+    /// in supersampled coordinates.
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    /// Blits a rectangle of pixels, with location and size specified
+    /// in supersampled coordinates.
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+
+private:
+    SkAlphaRuns fRuns;
+    int         fOffsetX;
+};
+
+SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                           const SkRegion& clip)
+        : BaseSuperBlitter(realBlitter, ir, clip) {
+    const int width = fWidth;
+
+    // extra one to store the zero at the end
+    fRuns.fRuns = (int16_t*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(int16_t));
+    fRuns.fAlpha = (uint8_t*)(fRuns.fRuns + width + 1);
+    fRuns.reset(width);
+
+    fOffsetX = 0;
+}
+
+void SuperBlitter::flush() {
+    if (fCurrIY >= fTop) {
+        if (!fRuns.empty()) {
+        //  SkDEBUGCODE(fRuns.dump();)
+            fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns);
+            fRuns.reset(fWidth);
+            fOffsetX = 0;
+        }
+        fCurrIY = fTop - 1;
+        SkDEBUGCODE(fCurrX = -1;)
+    }
+}
+
+/** coverage_to_partial_alpha() is being used by SkAlphaRuns, which
+    *accumulates* SCALE pixels worth of "alpha" in [0,(256/SCALE)]
+    to produce a final value in [0, 255] and handles clamping 256->255
+    itself, with the same (alpha - (alpha >> 8)) correction as
+    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;
+    aa -= aa >> (8 - SHIFT - 1);
+    return aa;
+#endif
+}
+
+/** coverage_to_exact_alpha() is being used by our blitter, which wants
+    a final value in [0, 255].
+*/
+static inline int coverage_to_exact_alpha(int aa) {
+    int alpha = (256 >> SHIFT) * aa;
+    // clamp 256->255
+    return alpha - (alpha >> 8);
+}
+
+void SuperBlitter::blitH(int x, int y, int width) {
+    SkASSERT(width > 0);
+
+    int iy = y >> SHIFT;
+    SkASSERT(iy >= fCurrIY);
+
+    x -= fSuperLeft;
+    // hack, until I figure out why my cubics (I think) go beyond the bounds
+    if (x < 0) {
+        width += x;
+        x = 0;
+    }
+
+#ifdef SK_DEBUG
+    SkASSERT(y != fCurrY || x >= fCurrX);
+#endif
+    SkASSERT(y >= fCurrY);
+    if (fCurrY != y) {
+        fOffsetX = 0;
+        fCurrY = y;
+    }
+    
+    if (iy != fCurrIY) {  // new scanline
+        this->flush();
+        fCurrIY = iy;
+    }
+
+    int start = x;
+    int stop = x + width;
+
+    SkASSERT(start >= 0 && stop > start);
+    // integer-pixel-aligned ends of blit, rounded out
+    int fb = start & MASK;
+    int fe = stop & MASK;
+    int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
+
+    if (n < 0) {
+        fb = fe - fb;
+        n = 0;
+        fe = 0;
+    } else {
+        if (fb == 0) {
+            n += 1;
+        } else {
+            fb = SCALE - fb;
+        }
+    }
+
+    fOffsetX = fRuns.add(x >> SHIFT, coverage_to_partial_alpha(fb),
+                         n, coverage_to_partial_alpha(fe),
+                         (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT),
+                         fOffsetX);
+
+#ifdef SK_DEBUG
+    fRuns.assertValid(y & MASK, (1 << (8 - SHIFT)));
+    fCurrX = x + width;
+#endif
+}
+
+static void set_left_rite_runs(SkAlphaRuns& runs, int ileft, U8CPU leftA,
+                               int n, U8CPU riteA) {
+    SkASSERT(leftA <= 0xFF);
+    SkASSERT(riteA <= 0xFF);
+
+    int16_t* run = runs.fRuns;
+    uint8_t* aa = runs.fAlpha;
+
+    if (ileft > 0) {
+        run[0] = ileft;
+        aa[0] = 0;
+        run += ileft;
+        aa += ileft;
+    }
+
+    SkASSERT(leftA < 0xFF);
+    if (leftA > 0) {
+        *run++ = 1;
+        *aa++ = leftA;
+    }
+
+    if (n > 0) {
+        run[0] = n;
+        aa[0] = 0xFF;
+        run += n;
+        aa += n;
+    }
+
+    SkASSERT(riteA < 0xFF);
+    if (riteA > 0) {
+        *run++ = 1;
+        *aa++ = riteA;
+    }
+    run[0] = 0;
+}
+
+void SuperBlitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(width > 0);
+    SkASSERT(height > 0);
+
+    // blit leading rows
+    while ((y & MASK)) {
+        this->blitH(x, y++, width);
+        if (--height <= 0) {
+            return;
+        }
+    }
+    SkASSERT(height > 0);
+
+    // Since this is a rect, instead of blitting supersampled rows one at a
+    // time and then resolving to the destination canvas, we can blit
+    // directly to the destintion canvas one row per SCALE supersampled rows.
+    int start_y = y >> SHIFT;
+    int stop_y = (y + height) >> SHIFT;
+    int count = stop_y - start_y;
+    if (count > 0) {
+        y += count << SHIFT;
+        height -= count << SHIFT;
+
+        // save original X for our tail blitH() loop at the bottom
+        int origX = x;
+
+        x -= fSuperLeft;
+        // hack, until I figure out why my cubics (I think) go beyond the bounds
+        if (x < 0) {
+            width += x;
+            x = 0;
+        }
+
+        // There is always a left column, a middle, and a right column.
+        // ileft is the destination x of the first pixel of the entire rect.
+        // xleft is (SCALE - # of covered supersampled pixels) in that
+        // destination pixel.
+        int ileft = x >> SHIFT;
+        int xleft = x & MASK;
+        // irite is the destination x of the last pixel of the OPAQUE section.
+        // xrite is the number of supersampled pixels extending beyond irite;
+        // xrite/SCALE should give us alpha.
+        int irite = (x + width) >> SHIFT;
+        int xrite = (x + width) & MASK;
+        if (!xrite) {
+            xrite = SCALE;
+            irite--;
+        }
+
+        // Need to call flush() to clean up pending draws before we
+        // even consider blitV(), since otherwise it can look nonmonotonic.
+        SkASSERT(start_y > fCurrIY);
+        this->flush();
+
+        int n = irite - ileft - 1;
+        if (n < 0) {
+            // If n < 0, we'll only have a single partially-transparent column
+            // of pixels to render.
+            xleft = xrite - xleft;
+            SkASSERT(xleft <= SCALE);
+            SkASSERT(xleft > 0);
+            xrite = 0;
+            fRealBlitter->blitV(ileft + fLeft, start_y, count,
+                coverage_to_exact_alpha(xleft));
+        } else {
+            // With n = 0, we have two possibly-transparent columns of pixels
+            // to render; with n > 0, we have opaque columns between them.
+
+            xleft = SCALE - xleft;
+
+            // Using coverage_to_exact_alpha is not consistent with blitH()
+            const int coverageL = coverage_to_exact_alpha(xleft);
+            const int coverageR = coverage_to_exact_alpha(xrite);
+
+            SkASSERT(coverageL > 0 || n > 0 || coverageR > 0);
+            SkASSERT((coverageL != 0) + n + (coverageR != 0) <= fWidth);
+
+            fRealBlitter->blitAntiRect(ileft + fLeft, start_y, n, count,
+                                       coverageL, coverageR);
+        }
+
+        // preamble for our next call to blitH()
+        fCurrIY = stop_y - 1;
+        fOffsetX = 0;
+        fCurrY = y - 1;
+        fRuns.reset(fWidth);
+        x = origX;
+    }
+
+    // catch any remaining few rows
+    SkASSERT(height <= MASK);
+    while (--height >= 0) {
+        this->blitH(x, y++, width);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Masked supersampling antialiased blitter.
+class MaskSuperBlitter : public BaseSuperBlitter {
+public:
+    MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                     const SkRegion& clip);
+    virtual ~MaskSuperBlitter() {
+        fRealBlitter->blitMask(fMask, fClipRect);
+    }
+
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+
+    static bool CanHandleRect(const SkIRect& bounds) {
+#ifdef FORCE_RLE
+        return false;
+#endif
+        int width = bounds.width();
+        int64_t rb = SkAlign4(width);
+        // use 64bits to detect overflow
+        int64_t storage = rb * bounds.height();
+
+        return (width <= MaskSuperBlitter::kMAX_WIDTH) &&
+               (storage <= MaskSuperBlitter::kMAX_STORAGE);
+    }
+
+private:
+    enum {
+#ifdef FORCE_SUPERMASK
+        kMAX_WIDTH = 2048,
+        kMAX_STORAGE = 1024 * 1024 * 2
+#else
+        kMAX_WIDTH = 32,    // so we don't try to do very wide things, where the RLE blitter would be faster
+        kMAX_STORAGE = 1024
+#endif
+    };
+
+    SkMask      fMask;
+    SkIRect     fClipRect;
+    // we add 1 because add_aa_span can write (unchanged) 1 extra byte at the end, rather than
+    // perform a test to see if stopAlpha != 0
+    uint32_t    fStorage[(kMAX_STORAGE >> 2) + 1];
+};
+
+MaskSuperBlitter::MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                                   const SkRegion& clip)
+        : BaseSuperBlitter(realBlitter, ir, clip) {
+    SkASSERT(CanHandleRect(ir));
+
+    fMask.fImage    = (uint8_t*)fStorage;
+    fMask.fBounds   = ir;
+    fMask.fRowBytes = ir.width();
+    fMask.fFormat   = SkMask::kA8_Format;
+
+    fClipRect = ir;
+    fClipRect.intersect(clip.getBounds());
+
+    // For valgrind, write 1 extra byte at the end so we don't read
+    // uninitialized memory. See comment in add_aa_span and fStorage[].
+    memset(fStorage, 0, fMask.fBounds.height() * fMask.fRowBytes + 1);
+}
+
+static void add_aa_span(uint8_t* alpha, U8CPU startAlpha) {
+    /*  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.
+    */
+    unsigned tmp = *alpha + startAlpha;
+    SkASSERT(tmp <= 256);
+    *alpha = SkToU8(tmp - (tmp >> 8));
+}
+
+static inline uint32_t quadplicate_byte(U8CPU value) {
+    uint32_t pair = (value << 8) | value;
+    return (pair << 16) | pair;
+}
+
+// minimum count before we want to setup an inner loop, adding 4-at-a-time
+#define MIN_COUNT_FOR_QUAD_LOOP  16
+
+static void add_aa_span(uint8_t* alpha, U8CPU startAlpha, int middleCount,
+                        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
+
+    if (middleCount >= MIN_COUNT_FOR_QUAD_LOOP) {
+        // loop until we're quad-byte aligned
+        while (SkTCast<intptr_t>(alpha) & 0x3) {
+            alpha[0] = SkToU8(alpha[0] + maxValue);
+            alpha += 1;
+            middleCount -= 1;
+        }
+
+        int bigCount = middleCount >> 2;
+        uint32_t* qptr = reinterpret_cast<uint32_t*>(alpha);
+        uint32_t qval = quadplicate_byte(maxValue);
+        do {
+            *qptr++ += qval;
+        } while (--bigCount > 0);
+
+        middleCount &= 3;
+        alpha = reinterpret_cast<uint8_t*> (qptr);
+        // fall through to the following while-loop
+    }
+
+    while (--middleCount >= 0) {
+        alpha[0] = SkToU8(alpha[0] + maxValue);
+        alpha += 1;
+    }
+
+    // potentially this can be off the end of our "legal" alpha values, but that
+    // 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);
+}
+
+void MaskSuperBlitter::blitH(int x, int y, int width) {
+    int iy = (y >> SHIFT);
+
+    SkASSERT(iy >= fMask.fBounds.fTop && iy < fMask.fBounds.fBottom);
+    iy -= fMask.fBounds.fTop;   // make it relative to 0
+
+    // This should never happen, but it does.  Until the true cause is
+    // discovered, let's skip this span instead of crashing.
+    // See http://crbug.com/17569.
+    if (iy < 0) {
+        return;
+    }
+
+#ifdef SK_DEBUG
+    {
+        int ix = x >> SHIFT;
+        SkASSERT(ix >= fMask.fBounds.fLeft && ix < fMask.fBounds.fRight);
+    }
+#endif
+
+    x -= (fMask.fBounds.fLeft << SHIFT);
+
+    // hack, until I figure out why my cubics (I think) go beyond the bounds
+    if (x < 0) {
+        width += x;
+        x = 0;
+    }
+
+    uint8_t* row = fMask.fImage + iy * fMask.fRowBytes + (x >> SHIFT);
+
+    int start = x;
+    int stop = x + width;
+
+    SkASSERT(start >= 0 && stop > start);
+    int fb = start & MASK;
+    int fe = stop & MASK;
+    int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
+
+
+    if (n < 0) {
+        SkASSERT(row >= fMask.fImage);
+        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),
+                    n, coverage_to_partial_alpha(fe),
+                    (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT));
+    }
+
+#ifdef SK_DEBUG
+    fCurrX = x + width;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool fitsInsideLimit(const SkRect& r, SkScalar max) {
+    const SkScalar min = -max;
+    return  r.fLeft > min && r.fTop > min &&
+            r.fRight < max && r.fBottom < max;
+}
+
+static int overflows_short_shift(int value, int shift) {
+    const int s = 16 + shift;
+    return (value << s >> s) - value;
+}
+
+/**
+  Would any of the coordinates of this rectangle not fit in a short,
+  when left-shifted by shift?
+*/
+static int rect_overflows_short_shift(SkIRect rect, int shift) {
+    SkASSERT(!overflows_short_shift(8191, SHIFT));
+    SkASSERT(overflows_short_shift(8192, SHIFT));
+    SkASSERT(!overflows_short_shift(32767, 0));
+    SkASSERT(overflows_short_shift(32768, 0));
+
+    // Since we expect these to succeed, we bit-or together
+    // for a tiny extra bit of speed.
+    return overflows_short_shift(rect.fLeft, SHIFT) |
+           overflows_short_shift(rect.fRight, SHIFT) |
+           overflows_short_shift(rect.fTop, SHIFT) |
+           overflows_short_shift(rect.fBottom, SHIFT);
+}
+
+static bool safeRoundOut(const SkRect& src, SkIRect* dst, int32_t maxInt) {
+#ifdef SK_SCALAR_IS_FIXED
+    // the max-int (shifted) is exactly what we want to compare against, to know
+    // if we can survive shifting our fixed-point coordinates
+    const SkFixed maxScalar = maxInt;
+#else
+    const SkScalar maxScalar = SkIntToScalar(maxInt);
+#endif
+    if (fitsInsideLimit(src, maxScalar)) {
+        src.roundOut(dst);
+        return true;
+    }
+    return false;
+}
+
+void SkScan::AntiFillPath(const SkPath& path, const SkRegion& origClip,
+                          SkBlitter* blitter, bool forceRLE) {
+    if (origClip.isEmpty()) {
+        return;
+    }
+
+    SkIRect ir;
+
+    if (!safeRoundOut(path.getBounds(), &ir, SK_MaxS32 >> SHIFT)) {
+#if 0
+        const SkRect& r = path.getBounds();
+        SkDebugf("--- bounds can't fit in SkIRect\n", r.fLeft, r.fTop, r.fRight, r.fBottom);
+#endif
+        return;
+    }
+    if (ir.isEmpty()) {
+        if (path.isInverseFillType()) {
+            blitter->blitRegion(origClip);
+        }
+        return;
+    }
+
+    // If the intersection of the path bounds and the clip bounds
+    // will overflow 32767 when << by SHIFT, we can't supersample,
+    // so draw without antialiasing.
+    SkIRect clippedIR;
+    if (path.isInverseFillType()) {
+       // If the path is an inverse fill, it's going to fill the entire
+       // clip, and we care whether the entire clip exceeds our limits.
+       clippedIR = origClip.getBounds();
+    } else {
+       if (!clippedIR.intersect(ir, origClip.getBounds())) {
+           return;
+       }
+    }
+    if (rect_overflows_short_shift(clippedIR, SHIFT)) {
+        SkScan::FillPath(path, origClip, blitter);
+        return;
+    }
+
+    // Our antialiasing can't handle a clip larger than 32767, so we restrict
+    // the clip to that limit here. (the runs[] uses int16_t for its index).
+    //
+    // A more general solution (one that could also eliminate the need to
+    // disable aa based on ir bounds (see overflows_short_shift) would be
+    // to tile the clip/target...
+    SkRegion tmpClipStorage;
+    const SkRegion* clipRgn = &origClip;
+    {
+        static const int32_t kMaxClipCoord = 32767;
+        const SkIRect& bounds = origClip.getBounds();
+        if (bounds.fRight > kMaxClipCoord || bounds.fBottom > kMaxClipCoord) {
+            SkIRect limit = { 0, 0, kMaxClipCoord, kMaxClipCoord };
+            tmpClipStorage.op(origClip, limit, SkRegion::kIntersect_Op);
+            clipRgn = &tmpClipStorage;
+        }
+    }
+    // for here down, use clipRgn, not origClip
+
+    SkScanClipper   clipper(blitter, clipRgn, ir);
+    const SkIRect*  clipRect = clipper.getClipRect();
+
+    if (clipper.getBlitter() == NULL) { // clipped out
+        if (path.isInverseFillType()) {
+            blitter->blitRegion(*clipRgn);
+        }
+        return;
+    }
+
+    // now use the (possibly wrapped) blitter
+    blitter = clipper.getBlitter();
+
+    if (path.isInverseFillType()) {
+        sk_blit_above(blitter, ir, *clipRgn);
+    }
+
+    SkIRect superRect, *superClipRect = NULL;
+
+    if (clipRect) {
+        superRect.set(  clipRect->fLeft << SHIFT, clipRect->fTop << SHIFT,
+                        clipRect->fRight << SHIFT, clipRect->fBottom << SHIFT);
+        superClipRect = &superRect;
+    }
+
+    SkASSERT(SkIntToScalar(ir.fTop) <= path.getBounds().fTop);
+
+    // MaskSuperBlitter can't handle drawing outside of ir, so we can't use it
+    // if we're an inverse filltype
+    if (!path.isInverseFillType() && MaskSuperBlitter::CanHandleRect(ir) && !forceRLE) {
+        MaskSuperBlitter    superBlit(blitter, ir, *clipRgn);
+        SkASSERT(SkIntToScalar(ir.fTop) <= path.getBounds().fTop);
+        sk_fill_path(path, superClipRect, &superBlit, ir.fTop, ir.fBottom, SHIFT, *clipRgn);
+    } else {
+        SuperBlitter    superBlit(blitter, ir, *clipRgn);
+        sk_fill_path(path, superClipRect, &superBlit, ir.fTop, ir.fBottom, SHIFT, *clipRgn);
+    }
+
+    if (path.isInverseFillType()) {
+        sk_blit_below(blitter, ir, *clipRgn);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkRasterClip.h"
+
+void SkScan::FillPath(const SkPath& path, const SkRasterClip& clip,
+                          SkBlitter* blitter) {
+    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);
+    }
+}
+
+void SkScan::AntiFillPath(const SkPath& path, const SkRasterClip& clip,
+                          SkBlitter* blitter) {
+    if (clip.isEmpty()) {
+        return;
+    }
+
+    if (clip.isBW()) {
+        AntiFillPath(path, clip.bwRgn(), blitter);
+    } else {
+        SkRegion        tmp;
+        SkAAClipBlitter aaBlitter;
+
+        tmp.setRect(clip.getBounds());
+        aaBlitter.init(blitter, &clip.aaRgn());
+        SkScan::AntiFillPath(path, tmp, &aaBlitter, true);
+    }
+}
+
diff --git a/legacy/src/core/SkScan_Antihair.cpp b/legacy/src/core/SkScan_Antihair.cpp
new file mode 100644
index 0000000..3b28634
--- /dev/null
+++ b/legacy/src/core/SkScan_Antihair.cpp
@@ -0,0 +1,847 @@
+
+/*
+ * 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 "SkScan.h"
+#include "SkBlitter.h"
+#include "SkColorPriv.h"
+#include "SkLineClipper.h"
+#include "SkRasterClip.h"
+#include "SkFDot6.h"
+
+/*  Our attempt to compute the worst case "bounds" for the horizontal and
+    vertical cases has some numerical bug in it, and we sometimes undervalue
+    our extends. The bug is that when this happens, we will set the clip to
+    NULL (for speed), and thus draw outside of the clip by a pixel, which might
+    only look bad, but it might also access memory outside of the valid range
+    allcoated for the device bitmap.
+
+    This define enables our fix to outset our "bounds" by 1, thus avoiding the
+    chance of the bug, but at the cost of sometimes taking the rectblitter
+    case (i.e. not setting the clip to NULL) when we might not actually need
+    to. If we can improve/fix the actual calculations, then we can remove this
+    step.
+ */
+#define OUTSET_BEFORE_CLIP_TEST     true
+
+#define HLINE_STACK_BUFFER      100
+
+static inline int SmallDot6Scale(int value, int dot6) {
+    SkASSERT((int16_t)value == value);
+    SkASSERT((unsigned)dot6 <= 64);
+    return SkMulS16(value, dot6) >> 6;
+}
+
+//#define TEST_GAMMA
+
+#ifdef TEST_GAMMA
+    static uint8_t gGammaTable[256];
+    #define ApplyGamma(table, alpha)    (table)[alpha]
+
+    static void build_gamma_table() {
+        static bool gInit = false;
+
+        if (gInit == false) {
+            for (int i = 0; i < 256; i++) {
+                SkFixed n = i * 257;
+                n += n >> 15;
+                SkASSERT(n >= 0 && n <= SK_Fixed1);
+                n = SkFixedSqrt(n);
+                n = n * 255 >> 16;
+            //  SkDebugf("morph %d -> %d\n", i, n);
+                gGammaTable[i] = SkToU8(n);
+            }
+            gInit = true;
+        }
+    }
+#else
+    #define ApplyGamma(table, alpha)    SkToU8(alpha)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
+                               U8CPU alpha) {
+    SkASSERT(count > 0);
+
+    int16_t runs[HLINE_STACK_BUFFER + 1];
+    uint8_t  aa[HLINE_STACK_BUFFER];
+
+    aa[0] = ApplyGamma(gGammaTable, alpha);
+    do {
+        int n = count;
+        if (n > HLINE_STACK_BUFFER) {
+            n = HLINE_STACK_BUFFER;
+        }
+        runs[0] = SkToS16(n);
+        runs[n] = 0;
+        blitter->blitAntiH(x, y, aa, runs);
+        x += n;
+        count -= n;
+    } 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;
+
+    int y = fy >> 16;
+    uint8_t  a = (uint8_t)(fy >> 8);
+
+    // lower line
+    unsigned ma = SmallDot6Scale(a, mod64);
+    if (ma) {
+        call_hline_blitter(blitter, x, y, count, ma);
+    }
+
+    // upper line
+    ma = SmallDot6Scale(255 - a, mod64);
+    if (ma) {
+        call_hline_blitter(blitter, x, y - 1, count, ma);
+    }
+    
+    return fy - SK_Fixed1/2;
+}
+
+static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy,
+                      SkBlitter* blitter, int mod64) {
+    SkASSERT(x < stopx);
+
+#ifdef TEST_GAMMA
+    const uint8_t* gamma = gGammaTable;
+#endif
+    int16_t runs[2];
+    uint8_t  aa[1];
+
+    runs[0] = 1;
+    runs[1] = 0;
+
+    fy += SK_Fixed1/2;
+    do {
+        int lower_y = fy >> 16;
+        uint8_t  a = (uint8_t)(fy >> 8);
+        unsigned ma = SmallDot6Scale(a, mod64);
+        if (ma) {
+            aa[0] = ApplyGamma(gamma, ma);
+            blitter->blitAntiH(x, lower_y, aa, runs);
+            // the clipping blitters might edit runs, but should not affect us
+            SkASSERT(runs[0] == 1);
+            SkASSERT(runs[1] == 0);
+        }
+        ma = SmallDot6Scale(255 - a, mod64);
+        if (ma) {
+            aa[0] = ApplyGamma(gamma, ma);
+            blitter->blitAntiH(x, lower_y - 1, aa, runs);
+            // the clipping blitters might edit runs, but should not affect us
+            SkASSERT(runs[0] == 1);
+            SkASSERT(runs[1] == 0);
+        }
+        fy += dy;
+    } 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));
+    }
+    ma = SmallDot6Scale(255 - a, mod64);
+    if (ma) {
+        blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
+    }
+    
+    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];
+
+    runs[0] = 1;
+    runs[2] = 0;
+
+    fx += SK_Fixed1/2;
+    do {
+        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));
+        // 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);
+        // 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;
+}
+
+typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart,
+                            SkFixed slope, SkBlitter*, int);
+
+static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
+    SkASSERT((a << 16 >> 16) == a);
+    SkASSERT(b != 0);
+    return (a << 16) / b;
+}
+
+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
+
+    if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
+        /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
+            precise, but avoids overflowing the intermediate result if the
+            values are huge. A better fix might be to clip the original pts
+            directly (i.e. do the divide), so we don't spend time subdividing
+            huge lines at all.
+         */
+        int hx = (x0 >> 1) + (x1 >> 1);
+        int hy = (y0 >> 1) + (y1 >> 1);
+        do_anti_hairline(x0, y0, hx, hy, clip, blitter);
+        do_anti_hairline(hx, hy, x1, y1, clip, blitter);
+        return;
+    }
+
+    int         scaleStart, scaleStop;
+    int         istart, istop;
+    SkFixed     fstart, slope; 
+    LineProc    proc;
+
+    if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal
+        if (x0 > x1) {    // we want to go left-to-right
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+
+        istart = SkFDot6Floor(x0);
+        istop = SkFDot6Ceil(x1);
+        fstart = SkFDot6ToFixed(y0);
+        if (y0 == y1) {   // completely horizontal, take fast case
+            slope = 0;
+            proc = hline;
+        } else {
+            slope = fastfixdiv(y1 - y0, x1 - x0);
+            SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
+            fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
+            proc = horish;
+        }
+        
+        SkASSERT(istop > istart);
+        if (istop - istart == 1) {
+            scaleStart = x1 - x0;
+            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+            scaleStop = 0;
+        } else {
+            scaleStart = 64 - (x0 & 63);
+            scaleStop = x1 & 63;
+        }
+
+        if (clip){
+            if (istart >= clip->fRight || istop <= clip->fLeft) {
+                return;
+            }
+            if (istart < clip->fLeft) {
+                fstart += slope * (clip->fLeft - istart);
+                istart = clip->fLeft;
+                scaleStart = 64;
+            }
+            if (istop > clip->fRight) {
+                istop = clip->fRight;
+                scaleStop = 64;
+            }
+            SkASSERT(istart <= istop);
+            if (istart == istop) {
+                return;
+            }
+            // now test if our Y values are completely inside the clip
+            int top, bottom;
+            if (slope >= 0) { // T2B
+                top = SkFixedFloor(fstart - SK_FixedHalf);
+                bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
+            } else {           // B2T
+                bottom = SkFixedCeil(fstart + SK_FixedHalf);
+                top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
+            }
+#ifdef OUTSET_BEFORE_CLIP_TEST
+            top -= 1;
+            bottom += 1;
+#endif
+            if (top >= clip->fBottom || bottom <= clip->fTop) {
+                return;
+            }
+            if (clip->fTop <= top && clip->fBottom >= bottom) {
+                clip = NULL;
+            }
+        }
+    } else {   // mostly vertical
+        if (y0 > y1) {  // we want to go top-to-bottom
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+
+        istart = SkFDot6Floor(y0);
+        istop = SkFDot6Ceil(y1);
+        fstart = SkFDot6ToFixed(x0);
+        if (x0 == x1) {
+            if (y0 == y1) { // are we zero length?
+                return;     // nothing to do
+            }
+            slope = 0;
+            proc = vline;
+        } else {
+            slope = fastfixdiv(x1 - x0, y1 - y0);
+            SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
+            fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
+            proc = vertish;
+        }
+
+        SkASSERT(istop > istart);
+        if (istop - istart == 1) {
+            scaleStart = y1 - y0;
+            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+            scaleStop = 0;
+        } else {
+            scaleStart = 64 - (y0 & 63);
+            scaleStop = y1 & 63;
+        }
+        
+        if (clip) {
+            if (istart >= clip->fBottom || istop <= clip->fTop) {
+                return;
+            }
+            if (istart < clip->fTop) {
+                fstart += slope * (clip->fTop - istart);
+                istart = clip->fTop;
+                scaleStart = 64;
+            }
+            if (istop > clip->fBottom) {
+                istop = clip->fBottom;
+                scaleStop = 64;
+            }
+            SkASSERT(istart <= istop);
+            if (istart == istop)
+                return;
+
+            // now test if our X values are completely inside the clip
+            int left, right;
+            if (slope >= 0) { // L2R
+                left = SkFixedFloor(fstart - SK_FixedHalf);
+                right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
+            } else {           // R2L
+                right = SkFixedCeil(fstart + SK_FixedHalf);
+                left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
+            }
+#ifdef OUTSET_BEFORE_CLIP_TEST
+            left -= 1;
+            right += 1;
+#endif
+            if (left >= clip->fRight || right <= clip->fLeft) {
+                return;
+            }
+            if (clip->fLeft <= left && clip->fRight >= right) {
+                clip = NULL;
+            }
+        }
+    }
+
+    SkRectClipBlitter   rectClipper;
+    if (clip) {
+        rectClipper.init(blitter, *clip);
+        blitter = &rectClipper;
+    }
+    
+    fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
+    istart += 1;
+    int fullSpans = istop - istart - (scaleStop > 0);
+    if (fullSpans > 0) {
+        fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
+    }
+    if (scaleStop > 0) {
+        proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
+    }
+}
+
+void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
+                             const SkRegion* clip, SkBlitter* blitter) {
+    if (clip && clip->isEmpty()) {
+        return;
+    }
+
+    SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
+
+#ifdef TEST_GAMMA
+    build_gamma_table();
+#endif
+
+    SkPoint pts[2] = { pt0, pt1 };
+
+    if (clip) {
+        SkRect clipBounds;
+        clipBounds.set(clip->getBounds());
+        /*  We perform integral clipping later on, but we do a scalar clip first
+            to ensure that our coordinates are expressible in fixed/integers.
+
+            antialiased hairlines can draw up to 1/2 of a pixel outside of
+            their bounds, so we need to outset the clip before calling the
+            clipper. To make the numerics safer, we outset by a whole pixel,
+            since the 1/2 pixel boundary is important to the antihair blitter,
+            we don't want to risk numerical fate by chopping on that edge.
+         */
+        clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
+
+        if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
+            return;
+        }
+    }
+        
+    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
+    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
+    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
+    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
+
+    if (clip) {
+        SkFDot6 left = SkMin32(x0, x1);
+        SkFDot6 top = SkMin32(y0, y1);
+        SkFDot6 right = SkMax32(x0, x1);
+        SkFDot6 bottom = SkMax32(y0, y1);
+        SkIRect ir;
+
+        ir.set( SkFDot6Floor(left) - 1,
+                SkFDot6Floor(top) - 1,
+                SkFDot6Ceil(right) + 1,
+                SkFDot6Ceil(bottom) + 1);
+
+        if (clip->quickReject(ir)) {
+            return;
+        }
+        if (!clip->quickContains(ir)) {
+            SkRegion::Cliperator iter(*clip, ir);
+            const SkIRect*       r = &iter.rect();
+
+            while (!iter.done()) {
+                do_anti_hairline(x0, y0, x1, y1, r, blitter);
+                iter.next();
+            }
+            return;
+        }
+        // fall through to no-clip case
+    }
+    do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
+}
+
+void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
+                          SkBlitter* blitter) {
+    SkPoint p0, p1;
+
+    p0.set(rect.fLeft, rect.fTop);
+    p1.set(rect.fRight, rect.fTop);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+    p0.set(rect.fRight, rect.fBottom);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+    p1.set(rect.fLeft, rect.fBottom);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+    p0.set(rect.fLeft, rect.fTop);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef int FDot8;  // 24.8 integer fixed point
+
+static inline FDot8 SkFixedToFDot8(SkFixed x) {
+    return (x + 0x80) >> 8;
+}
+
+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;
+    }
+
+    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, SkAlphaMul(alpha, R & 0xFF));
+    }
+}
+
+static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
+                         bool fillInner) {
+    // check for empty now that we're in our reduced precision space
+    if (L >= R || T >= B) {
+        return;
+    }
+    int top = T >> 8;
+    if (top == ((B - 1) >> 8)) {   // just one scanline high
+        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) {
+        int left = L >> 8;
+        if (left == ((R - 1) >> 8)) {   // just 1-pixel wide
+            blitter->blitV(left, top, height, R - L - 1);
+        } else {
+            if (L & 0xFF) {
+                blitter->blitV(left, top, height, 256 - (L & 0xFF));
+                left += 1;
+            }
+            int rite = R >> 8;
+            int width = rite - left;
+            if (width > 0 && fillInner) {
+                blitter->blitRect(left, top, width, height);
+            }
+            if (R & 0xFF) {
+                blitter->blitV(rite, top, height, R & 0xFF);
+            }
+        }
+    }
+    
+    if (B & 0xFF) {
+        do_scanline(L, bot, R, B & 0xFF, blitter);
+    }
+}
+
+static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
+    antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
+                 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
+                 blitter, true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
+                          SkBlitter* blitter) {
+    if (NULL == clip) {
+        antifillrect(xr, blitter);
+    } else {
+        SkIRect outerBounds;
+        XRect_roundOut(xr, &outerBounds);
+
+        if (clip->isRect()) {
+            const SkIRect& clipBounds = clip->getBounds();
+
+            if (clipBounds.contains(outerBounds)) {
+                antifillrect(xr, blitter);
+            } else {
+                SkXRect tmpR;
+                // this keeps our original edges fractional
+                XRect_set(&tmpR, clipBounds);
+                if (tmpR.intersect(xr)) {
+                    antifillrect(tmpR, blitter);
+                }
+            }
+        } 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)) {
+                    antifillrect(tmpR, blitter);
+                }
+                clipper.next();
+            }
+        }
+    }
+}
+
+void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
+                           SkBlitter* blitter) {
+    if (clip.isBW()) {
+        AntiFillXRect(xr, &clip.bwRgn(), blitter);
+    } else {
+        SkIRect outerBounds;
+        XRect_roundOut(xr, &outerBounds);
+
+        if (clip.quickContains(outerBounds)) {
+            AntiFillXRect(xr, NULL, blitter);
+        } else {
+            SkAAClipBlitterWrapper wrapper(clip, blitter);
+            blitter = wrapper.getBlitter();
+
+            AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
+        }
+    }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+/*  This guy takes a float-rect, but with the key improvement that it has
+    already been clipped, so we know that it is safe to convert it into a
+    XRect (fixedpoint), as it won't overflow.
+*/
+static void antifillrect(const SkRect& r, SkBlitter* blitter) {
+    SkXRect xr;
+    
+    XRect_set(&xr, r);
+    antifillrect(xr, blitter);
+}
+
+/*  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.
+*/
+void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
+                          SkBlitter* blitter) {
+    if (clip) {
+        SkRect newR;
+        newR.set(clip->getBounds());
+        if (!newR.intersect(origR)) {
+            return;
+        }
+
+        SkIRect outerBounds;
+        newR.roundOut(&outerBounds);
+        
+        if (clip->isRect()) {
+            antifillrect(newR, blitter);
+        } else {
+            SkRegion::Cliperator clipper(*clip, outerBounds);
+            while (!clipper.done()) {
+                newR.set(clipper.rect());
+                if (newR.intersect(origR)) {
+                    antifillrect(newR, blitter);
+                }
+                clipper.next();
+            }
+        }
+    } else {
+        antifillrect(origR, blitter);
+    }
+}
+
+void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
+                          SkBlitter* blitter) {
+    if (clip.isBW()) {
+        AntiFillRect(r, &clip.bwRgn(), blitter);
+    } else {
+        SkAAClipBlitterWrapper wrap(clip, blitter);
+        AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
+    }
+}
+
+#endif // SK_SCALAR_IS_FLOAT
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b)
+
+// calls blitRect() if the rectangle is non-empty
+static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
+    if (L < R && T < B) {
+        blitter->blitRect(L, T, R - L, B - T);
+    }
+}
+
+static inline FDot8 SkScalarToFDot8(SkScalar x) {
+#ifdef SK_SCALAR_IS_FLOAT
+    return (int)(x * 256);
+#else
+    return x >> 8;
+#endif
+}
+
+static inline int FDot8Floor(FDot8 x) {
+    return x >> 8;
+}
+
+static inline int FDot8Ceil(FDot8 x) {
+    return (x + 0xFF) >> 8;
+}
+
+// 1 - (1 - a)*(1 - b)
+static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
+    // need precise rounding (not just SkAlphaMul) so that values like
+    // a=228, b=252 don't overflow the result
+    return SkToU8(a + b - SkAlphaMulRound(a, b));
+}
+
+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));
+    }
+}
+
+static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
+                            SkBlitter* blitter) {
+    SkASSERT(L < R && T < B);
+
+    int top = T >> 8;
+    if (top == ((B - 1) >> 8)) {   // just one scanline high
+        inner_scanline(L, top, R, B - T, 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) {
+        if (L & 0xFF) {
+            blitter->blitV(L >> 8, top, height, L & 0xFF);
+        }
+        if (R & 0xFF) {
+            blitter->blitV(R >> 8, top, height, ~R & 0xFF);
+        }
+    }
+    
+    if (B & 0xFF) {
+        inner_scanline(L, bot, R, ~B & 0xFF, blitter);
+    }
+}
+
+void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
+                           const SkRegion* clip, SkBlitter* blitter) {
+    SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
+
+    SkScalar rx = SkScalarHalf(strokeSize.fX);
+    SkScalar ry = SkScalarHalf(strokeSize.fY);
+
+    // outset by the radius
+    FDot8 L = SkScalarToFDot8(r.fLeft - rx);
+    FDot8 T = SkScalarToFDot8(r.fTop - ry);
+    FDot8 R = SkScalarToFDot8(r.fRight + rx);
+    FDot8 B = SkScalarToFDot8(r.fBottom + ry);
+
+    SkIRect outer;
+    // set outer to the outer rect of the outer section
+    outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
+
+    SkBlitterClipper clipper;
+    if (clip) {
+        if (clip->quickReject(outer)) {
+            return;
+        }
+        if (!clip->contains(outer)) {
+            blitter = clipper.apply(blitter, clip, &outer);
+        }
+        // now we can ignore clip for the rest of the function
+    }
+    
+    // stroke the outer hull
+    antifilldot8(L, T, R, B, blitter, false);
+
+    // set outer to the outer rect of the middle section
+    outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
+
+    // in case we lost a bit with diameter/2
+    rx = strokeSize.fX - rx;
+    ry = strokeSize.fY - ry;
+    // inset by the radius
+    L = SkScalarToFDot8(r.fLeft + rx);
+    T = SkScalarToFDot8(r.fTop + ry);
+    R = SkScalarToFDot8(r.fRight - rx);
+    B = SkScalarToFDot8(r.fBottom - ry);
+
+    if (L >= R || T >= B) {
+        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
+                      blitter);
+    } else {
+        SkIRect inner;
+        // set inner to the inner rect of the middle section
+        inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
+
+        // draw the frame in 4 pieces
+        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
+                      blitter);
+        fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
+                      blitter);
+        fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
+                      blitter);
+        fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
+                      blitter);
+
+        // now stroke the inner rect, which is similar to antifilldot8() except that
+        // it treats the fractional coordinates with the inverse bias (since its
+        // inner).
+        innerstrokedot8(L, T, R, B, blitter);
+    }
+}
+
+void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
+                           const SkRasterClip& clip, SkBlitter* blitter) {
+    if (clip.isBW()) {
+        AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
+    } else {
+        SkAAClipBlitterWrapper wrap(clip, blitter);
+        AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
+    }
+}
+
diff --git a/legacy/src/core/SkScan_Hairline.cpp b/legacy/src/core/SkScan_Hairline.cpp
new file mode 100644
index 0000000..412ec03
--- /dev/null
+++ b/legacy/src/core/SkScan_Hairline.cpp
@@ -0,0 +1,404 @@
+
+/*
+ * 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 "SkScan.h"
+#include "SkBlitter.h"
+#include "SkRasterClip.h"
+#include "SkFDot6.h"
+#include "SkLineClipper.h"
+
+static void horiline(int x, int stopx, SkFixed fy, SkFixed dy,
+                     SkBlitter* blitter) {
+    SkASSERT(x < stopx);
+
+    do {
+        blitter->blitH(x, fy >> 16, 1);
+        fy += dy;
+    } while (++x < stopx);
+}
+
+static void vertline(int y, int stopy, SkFixed fx, SkFixed dx,
+                     SkBlitter* blitter) {
+    SkASSERT(y < stopy);
+
+    do {
+        blitter->blitH(fx >> 16, y, 1);
+        fx += dx;
+    } while (++y < stopy);
+}
+
+void SkScan::HairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
+                         const SkRegion* clip, SkBlitter* blitter) {
+    SkBlitterClipper    clipper;
+    SkRect  r;
+    SkIRect clipR, ptsR;
+    SkPoint pts[2] = { pt0, pt1 };
+
+    if (clip) {
+        // Perform a clip in scalar space, so we catch huge values which might
+        // be missed after we convert to SkFDot6 (overflow)
+        r.set(clip->getBounds());
+        if (!SkLineClipper::IntersectLine(pts, r, pts)) {
+            return;
+        }
+    }
+
+    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
+    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
+    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
+    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
+    
+    if (clip) {
+        // now perform clipping again, as the rounding to dot6 can wiggle us
+        // our rects are really dot6 rects, but since we've already used
+        // lineclipper, we know they will fit in 32bits (26.6)
+        const SkIRect& bounds = clip->getBounds();
+
+        clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
+                  SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
+        ptsR.set(x0, y0, x1, y1);
+        ptsR.sort();
+
+        // outset the right and bottom, to account for how hairlines are
+        // actually drawn, which may hit the pixel to the right or below of
+        // the coordinate
+        ptsR.fRight += SK_FDot6One;
+        ptsR.fBottom += SK_FDot6One;
+
+        if (!SkIRect::Intersects(ptsR, clipR)) {
+            return;
+        }
+        if (clip->isRect() && clipR.contains(ptsR)) {
+            clip = NULL;
+        } else {
+            blitter = clipper.apply(blitter, clip);
+        }
+    }
+
+    SkFDot6 dx = x1 - x0;
+    SkFDot6 dy = y1 - y0;
+
+    if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
+        if (x0 > x1) {   // we want to go left-to-right
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+        int ix0 = SkFDot6Round(x0);
+        int ix1 = SkFDot6Round(x1);
+        if (ix0 == ix1) {// too short to draw
+            return;
+        }
+
+        SkFixed slope = SkFixedDiv(dy, dx);
+        SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
+
+        horiline(ix0, ix1, startY, slope, blitter);
+    } else {              // mostly vertical
+        if (y0 > y1) {   // we want to go top-to-bottom
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+        int iy0 = SkFDot6Round(y0);
+        int iy1 = SkFDot6Round(y1);
+        if (iy0 == iy1) { // too short to draw
+            return;
+        }
+
+        SkFixed slope = SkFixedDiv(dx, dy);
+        SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
+
+        vertline(iy0, iy1, startX, slope, blitter);
+    }
+}
+
+// we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
+// and double-hit the top-left.
+// TODO: handle huge coordinates on rect (before calling SkScalarToFixed)
+void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip,
+                      SkBlitter* blitter) {
+    SkAAClipBlitterWrapper wrapper;
+    SkBlitterClipper    clipper;
+    SkIRect             r;
+
+    r.set(SkScalarToFixed(rect.fLeft) >> 16,
+          SkScalarToFixed(rect.fTop) >> 16,
+          (SkScalarToFixed(rect.fRight) >> 16) + 1,
+          (SkScalarToFixed(rect.fBottom) >> 16) + 1);
+
+    if (clip.quickReject(r)) {
+        return;
+    }
+    if (!clip.quickContains(r)) {
+        const SkRegion* clipRgn;
+        if (clip.isBW()) {
+            clipRgn = &clip.bwRgn();
+        } else {
+            wrapper.init(clip, blitter);
+            clipRgn = &wrapper.getRgn();
+            blitter = wrapper.getBlitter();
+        }
+        blitter = clipper.apply(blitter, clipRgn);
+    }
+
+    int width = r.width();
+    int height = r.height();
+    
+    if ((width | height) == 0) {
+        return;
+    }
+    if (width <= 2 || height <= 2) {
+        blitter->blitRect(r.fLeft, r.fTop, width, height);
+        return;
+    }
+    // if we get here, we know we have 4 segments to draw
+    blitter->blitH(r.fLeft, r.fTop, width);                     // top
+    blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2);      // left
+    blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
+    blitter->blitH(r.fLeft, r.fBottom - 1, width);              // bottom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPath.h"
+#include "SkGeometry.h"
+
+static bool quad_too_curvy(const SkPoint pts[3]) {
+    return true;
+}
+
+static int compute_int_quad_dist(const SkPoint pts[3]) {
+    // compute the vector between the control point ([1]) and the middle of the
+    // line connecting the start and end ([0] and [2])
+    SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
+    SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
+    // we want everyone to be positive
+    dx = SkScalarAbs(dx);
+    dy = SkScalarAbs(dy);
+    // convert to whole pixel values (use ceiling to be conservative)
+    int idx = SkScalarCeil(dx);
+    int idy = SkScalarCeil(dy);
+    // use the cheap approx for distance
+    if (idx > idy) {
+        return idx + (idy >> 1);
+    } else {
+        return idy + (idx >> 1);
+    }
+}
+
+static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level,
+                     void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*))
+{
+#if 1
+    if (level > 0 && quad_too_curvy(pts))
+    {
+        SkPoint tmp[5];
+
+        SkChopQuadAtHalf(pts, tmp);
+        hairquad(tmp, clip, blitter, level - 1, lineproc);
+        hairquad(&tmp[2], clip, blitter, level - 1, lineproc);
+    }
+    else
+        lineproc(pts[0], pts[2], clip, blitter);
+#else
+    int count = 1 << level;
+    const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
+    SkScalar t = dt;
+    SkPoint prevPt = pts[0];
+    for (int i = 1; i < count; i++) {
+        SkPoint nextPt;
+        SkEvalQuadAt(pts, t, &nextPt);
+        lineproc(prevPt, nextPt, clip, blitter);
+        t += dt;
+        prevPt = nextPt;
+    }
+    // draw the last line explicitly to 1.0, in case t didn't match that exactly
+    lineproc(prevPt, pts[2], clip, blitter);
+#endif
+}
+
+static bool cubic_too_curvy(const SkPoint pts[4])
+{
+    return true;
+}
+
+static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level,
+                      void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
+{
+    if (level > 0 && cubic_too_curvy(pts))
+    {
+        SkPoint tmp[7];
+
+        SkChopCubicAt(pts, tmp, SK_Scalar1/2);
+        haircubic(tmp, clip, blitter, level - 1, lineproc);
+        haircubic(&tmp[3], clip, blitter, level - 1, lineproc);
+    }
+    else
+        lineproc(pts[0], pts[3], clip, blitter);
+}
+
+#define kMaxCubicSubdivideLevel 6
+#define kMaxQuadSubdivideLevel  5
+
+static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
+                      void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
+{
+    if (path.isEmpty()) {
+        return;
+    }
+
+    SkAAClipBlitterWrapper wrap;
+    const SkIRect* clipR = NULL;
+    const SkRegion* clip = NULL;
+
+    {
+        SkIRect ibounds;
+        path.getBounds().roundOut(&ibounds);
+        ibounds.inset(-1, -1);
+
+        if (rclip.quickReject(ibounds)) {
+            return;
+        }
+        if (!rclip.quickContains(ibounds)) {
+            clipR = &rclip.getBounds();
+            if (rclip.isBW()) {
+                clip = &rclip.bwRgn();
+            } else {
+                wrap.init(rclip, blitter);
+                blitter = wrap.getBlitter();
+                clip = &wrap.getRgn();
+            }
+        }
+    }
+
+    SkPath::Iter    iter(path, false);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kLine_Verb:
+                lineproc(pts[0], pts[1], clip, blitter);
+                break;
+            case SkPath::kQuad_Verb: {
+                int d = compute_int_quad_dist(pts);
+                /*  quadratics approach the line connecting their start and end points
+                 4x closer with each subdivision, so we compute the number of
+                 subdivisions to be the minimum need to get that distance to be less
+                 than a pixel.
+                 */
+                int level = (33 - SkCLZ(d)) >> 1;
+    //          SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
+                // sanity check on level (from the previous version)
+                if (level > kMaxQuadSubdivideLevel) {
+                    level = kMaxQuadSubdivideLevel;
+                }
+                hairquad(pts, clip, blitter, level, lineproc);
+                break;
+            }
+            case SkPath::kCubic_Verb:
+                haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc);
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip,
+                      SkBlitter* blitter) {
+    hair_path(path, clip, blitter, SkScan::HairLineRgn);
+}
+
+void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip,
+                          SkBlitter* blitter) {
+    hair_path(path, clip, blitter, SkScan::AntiHairLineRgn);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
+                       const SkRasterClip& clip, SkBlitter* blitter) {
+    SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
+
+    if (strokeSize.fX < 0 || strokeSize.fY < 0) {
+        return;
+    }
+
+    const SkScalar dx = strokeSize.fX;
+    const SkScalar dy = strokeSize.fY;
+    SkScalar rx = SkScalarHalf(dx);
+    SkScalar ry = SkScalarHalf(dy);
+    SkRect   outer, tmp;
+
+    outer.set(r.fLeft - rx, r.fTop - ry,
+                r.fRight + rx, r.fBottom + ry);
+
+    if (r.width() <= dx || r.height() <= dx) {
+        SkScan::FillRect(outer, clip, blitter);
+        return;
+    }
+
+    tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
+    SkScan::FillRect(tmp, clip, blitter);
+    tmp.fTop = outer.fBottom - dy;
+    tmp.fBottom = outer.fBottom;
+    SkScan::FillRect(tmp, clip, blitter);
+
+    tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
+    SkScan::FillRect(tmp, clip, blitter);
+    tmp.fLeft = outer.fRight - dx;
+    tmp.fRight = outer.fRight;
+    SkScan::FillRect(tmp, clip, blitter);
+}
+
+void SkScan::HairLine(const SkPoint& p0, const SkPoint& p1,
+                      const SkRasterClip& clip, SkBlitter* blitter) {
+    if (clip.isBW()) {
+        HairLineRgn(p0, p1, &clip.bwRgn(), blitter);
+    } else {
+        const SkRegion* clipRgn = NULL;
+        SkRect r;
+        SkIRect ir;
+        r.set(p0.fX, p0.fY, p1.fX, p1.fY);
+        r.sort();
+        r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+        r.roundOut(&ir);
+
+        SkAAClipBlitterWrapper wrap;
+        if (!clip.quickContains(ir)) {
+            wrap.init(clip, blitter);
+            blitter = wrap.getBlitter();
+            clipRgn = &wrap.getRgn();
+        }
+        HairLineRgn(p0, p1, clipRgn, blitter);
+    }
+}
+
+void SkScan::AntiHairLine(const SkPoint& p0, const SkPoint& p1,
+                          const SkRasterClip& clip, SkBlitter* blitter) {
+    if (clip.isBW()) {
+        AntiHairLineRgn(p0, p1, &clip.bwRgn(), blitter);
+    } else {
+        const SkRegion* clipRgn = NULL;
+        SkRect r;
+        SkIRect ir;
+        r.set(p0.fX, p0.fY, p1.fX, p1.fY);
+        r.sort();
+        r.roundOut(&ir);
+        ir.inset(-1, -1);
+        
+        SkAAClipBlitterWrapper wrap;
+        if (!clip.quickContains(ir)) {
+            wrap.init(clip, blitter);
+            blitter = wrap.getBlitter();
+            clipRgn = &wrap.getRgn();
+        }
+        AntiHairLineRgn(p0, p1, clipRgn, blitter);
+    }
+}
diff --git a/legacy/src/core/SkScan_Path.cpp b/legacy/src/core/SkScan_Path.cpp
new file mode 100644
index 0000000..5b92ff9
--- /dev/null
+++ b/legacy/src/core/SkScan_Path.cpp
@@ -0,0 +1,694 @@
+/*
+ * 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 "SkScanPriv.h"
+#include "SkBlitter.h"
+#include "SkEdge.h"
+#include "SkEdgeBuilder.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+#include "SkQuadClipper.h"
+#include "SkRasterClip.h"
+#include "SkRegion.h"
+#include "SkTemplates.h"
+
+#define kEDGE_HEAD_Y    SK_MinS32
+#define kEDGE_TAIL_Y    SK_MaxS32
+
+#ifdef SK_DEBUG
+    static void validate_sort(const SkEdge* edge) {
+        int y = kEDGE_HEAD_Y;
+
+        while (edge->fFirstY != SK_MaxS32) {
+            edge->validate();
+            SkASSERT(y <= edge->fFirstY);
+
+            y = edge->fFirstY;
+            edge = edge->fNext;
+        }
+    }
+#else
+    #define validate_sort(edge)
+#endif
+
+static inline void remove_edge(SkEdge* edge) {
+    edge->fPrev->fNext = edge->fNext;
+    edge->fNext->fPrev = edge->fPrev;
+}
+
+static inline void swap_edges(SkEdge* prev, SkEdge* next) {
+    SkASSERT(prev->fNext == next && next->fPrev == prev);
+
+    // 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;
+    next->fNext = prev;
+    prev->fPrev = next;
+}
+
+static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y)) {
+    SkFixed x = edge->fX;
+
+    for (;;) {
+        SkEdge* prev = edge->fPrev;
+
+        // add 1 to curr_y since we may have added new edges (built from curves)
+        // that start on the next scanline
+        SkASSERT(prev && prev->fFirstY <= curr_y + 1);
+
+        if (prev->fX <= x) {
+            break;
+        }
+        swap_edges(prev, edge);
+    }
+}
+
+static void insert_new_edges(SkEdge* newEdge, int curr_y) {
+    SkASSERT(newEdge->fFirstY >= curr_y);
+
+    while (newEdge->fFirstY == curr_y) {
+        SkEdge* next = newEdge->fNext;
+        backward_insert_edge_based_on_x(newEdge  SkPARAM(curr_y));
+        newEdge = next;
+    }
+}
+
+#ifdef SK_DEBUG
+static void validate_edges_for_y(const SkEdge* edge, int curr_y) {
+    while (edge->fFirstY <= curr_y) {
+        SkASSERT(edge->fPrev && edge->fNext);
+        SkASSERT(edge->fPrev->fNext == edge);
+        SkASSERT(edge->fNext->fPrev == edge);
+        SkASSERT(edge->fFirstY <= edge->fLastY);
+
+        SkASSERT(edge->fPrev->fX <= edge->fX);
+        edge = edge->fNext;
+    }
+}
+#else
+    #define validate_edges_for_y(edge, curr_y)
+#endif
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : local variable used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+typedef void (*PrePostProc)(SkBlitter* blitter, int y, bool isStartOfScanline);
+#define PREPOST_START   true
+#define PREPOST_END     false
+
+static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType,
+                       SkBlitter* blitter, int start_y, int stop_y,
+                       PrePostProc proc) {
+    validate_sort(prevHead->fNext);
+
+    int curr_y = start_y;
+    // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
+    int windingMask = (fillType & 1) ? 1 : -1;
+
+    for (;;) {
+        int     w = 0;
+        int     left SK_INIT_TO_AVOID_WARNING;
+        bool    in_interval = false;
+        SkEdge* currE = prevHead->fNext;
+        SkFixed prevX = prevHead->fX;
+
+        validate_edges_for_y(currE, curr_y);
+
+        if (proc) {
+            proc(blitter, curr_y, PREPOST_START);    // pre-proc
+        }
+
+        while (currE->fFirstY <= curr_y) {
+            SkASSERT(currE->fLastY >= curr_y);
+
+            int x = SkFixedRoundToInt(currE->fX);
+            w += currE->fWinding;
+            if ((w & windingMask) == 0) { // we finished an interval
+                SkASSERT(in_interval);
+                int width = x - left;
+                SkASSERT(width >= 0);
+                if (width)
+                    blitter->blitH(left, curr_y, width);
+                in_interval = false;
+            } else if (!in_interval) {
+                left = x;
+                in_interval = true;
+            }
+
+            SkEdge* next = currE->fNext;
+            SkFixed newX;
+
+            if (currE->fLastY == curr_y) {    // are we done with this edge?
+                if (currE->fCurveCount < 0) {
+                    if (((SkCubicEdge*)currE)->updateCubic()) {
+                        SkASSERT(currE->fFirstY == curr_y + 1);
+
+                        newX = currE->fX;
+                        goto NEXT_X;
+                    }
+                } else if (currE->fCurveCount > 0) {
+                    if (((SkQuadraticEdge*)currE)->updateQuadratic()) {
+                        newX = currE->fX;
+                        goto NEXT_X;
+                    }
+                }
+                remove_edge(currE);
+            } else {
+                SkASSERT(currE->fLastY > curr_y);
+                newX = currE->fX + currE->fDX;
+                currE->fX = newX;
+            NEXT_X:
+                if (newX < prevX) { // ripple currE backwards until it is x-sorted
+                    backward_insert_edge_based_on_x(currE  SkPARAM(curr_y));
+                } else {
+                    prevX = newX;
+                }
+            }
+            currE = next;
+            SkASSERT(currE);
+        }
+
+        if (proc) {
+            proc(blitter, curr_y, PREPOST_END);    // post-proc
+        }
+
+        curr_y += 1;
+        if (curr_y >= stop_y) {
+            break;
+        }
+        // now currE points to the first edge with a Yint larger than curr_y
+        insert_new_edges(currE, curr_y);
+    }
+}
+
+// return true if we're done with this edge
+static bool update_edge(SkEdge* edge, int last_y) {
+    SkASSERT(edge->fLastY >= last_y);
+    if (last_y == edge->fLastY) {
+        if (edge->fCurveCount < 0) {
+            if (((SkCubicEdge*)edge)->updateCubic()) {
+                SkASSERT(edge->fFirstY == last_y + 1);
+                return false;
+            }
+        } else if (edge->fCurveCount > 0) {
+            if (((SkQuadraticEdge*)edge)->updateQuadratic()) {
+                SkASSERT(edge->fFirstY == last_y + 1);
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+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;
+
+#if 0
+    int local_top = leftE->fFirstY;
+    SkASSERT(local_top == riteE->fFirstY);
+#else
+    // our edge choppers for curves can result in the initial edges
+    // not lining up, so we take the max.
+    int local_top = SkMax32(leftE->fFirstY, riteE->fFirstY);
+#endif
+    SkASSERT(local_top >= start_y);
+    
+    int gLoops = 0;
+    for (;;) {
+        gLoops++;
+
+        SkASSERT(leftE->fFirstY <= stop_y);
+        SkASSERT(riteE->fFirstY <= stop_y);
+
+        if (leftE->fX > riteE->fX || (leftE->fX == riteE->fX &&
+                                      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;
+        SkFixed dRite = riteE->fDX;
+        int count = local_bot - local_top;
+        SkASSERT(count >= 0);
+        if (0 == (dLeft | dRite)) {
+            int L = SkFixedRoundToInt(left);
+            int R = SkFixedRoundToInt(rite);
+            if (L < R) {
+                count += 1;
+                blitter->blitRect(L, local_top, R - L, count);
+                left += count * dLeft;
+                rite += count * dRite;
+            }
+            local_top = local_bot + 1;
+        } else {
+            do {
+                int L = SkFixedRoundToInt(left);
+                int R = SkFixedRoundToInt(rite);
+                if (L < R) {
+                    blitter->blitH(L, local_top, R - L);
+                }
+                left += dLeft;
+                rite += dRite;
+                local_top += 1;
+            } while (--count >= 0);
+        }
+
+        leftE->fX = left;
+        riteE->fX = rite;
+
+        if (update_edge(leftE, local_bot)) {
+            if (currE->fFirstY >= stop_y) {
+                break;
+            }
+            leftE = currE;
+            currE = currE->fNext;
+        }
+        if (update_edge(riteE, local_bot)) {
+            if (currE->fFirstY >= stop_y) {
+                break;
+            }
+            riteE = currE;
+            currE = currE->fNext;
+        }
+        
+        SkASSERT(leftE);
+        SkASSERT(riteE);
+
+        // check our bottom clip
+        SkASSERT(local_top == local_bot + 1);
+        if (local_top >= stop_y) {
+            break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// this guy overrides blitH, and will call its proxy blitter with the inverse
+// of the spans it is given (clipped to the left/right of the cliprect)
+//
+// used to implement inverse filltypes on paths
+//
+class InverseBlitter : public SkBlitter {
+public:
+    void setBlitter(SkBlitter* blitter, const SkIRect& clip, int shift) {
+        fBlitter = blitter;
+        fFirstX = clip.fLeft << shift;
+        fLastX = clip.fRight << shift;
+    }
+    void prepost(int y, bool isStart) {
+        if (isStart) {
+            fPrevX = fFirstX;
+        } else {
+            int invWidth = fLastX - fPrevX;
+            if (invWidth > 0) {
+                fBlitter->blitH(fPrevX, y, invWidth);
+            }
+        }
+    }
+
+    // overrides
+    virtual void blitH(int x, int y, int width) {
+        int invWidth = x - fPrevX;
+        if (invWidth > 0) {
+            fBlitter->blitH(fPrevX, y, invWidth);
+        }
+        fPrevX = x + width;
+    }
+
+    // we do not expect to get called with these entrypoints
+    virtual void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) {
+        SkDEBUGFAIL("blitAntiH unexpected");
+    }
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) {
+        SkDEBUGFAIL("blitV unexpected");
+    }
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkDEBUGFAIL("blitRect unexpected");
+    }
+    virtual void blitMask(const SkMask&, const SkIRect& clip) {
+        SkDEBUGFAIL("blitMask unexpected");
+    }
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) {
+        SkDEBUGFAIL("justAnOpaqueColor unexpected");
+        return NULL;
+    }
+
+private:
+    SkBlitter*  fBlitter;
+    int         fFirstX, fLastX, fPrevX;
+};
+
+static void PrePostInverseBlitterProc(SkBlitter* blitter, int y, bool isStart) {
+    ((InverseBlitter*)blitter)->prepost(y, isStart);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+extern "C" {
+    static int edge_compare(const void* a, const void* b) {
+        const SkEdge* edgea = *(const SkEdge**)a;
+        const SkEdge* edgeb = *(const SkEdge**)b;
+
+        int valuea = edgea->fFirstY;
+        int valueb = edgeb->fFirstY;
+
+        if (valuea == valueb) {
+            valuea = edgea->fX;
+            valueb = edgeb->fX;
+        }
+
+        // this overflows if valuea >>> valueb or vice-versa
+        //     return valuea - valueb;
+        // do perform the slower but safe compares
+        return (valuea < valueb) ? -1 : (valuea > valueb);
+    }
+}
+
+static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) {
+    qsort(list, count, sizeof(SkEdge*), edge_compare);
+
+    // now make the edges linked in sorted order
+    for (int i = 1; i < count; i++) {
+        list[i - 1]->fNext = list[i];
+        list[i]->fPrev = list[i - 1];
+    }
+
+    *last = list[count - 1];
+    return list[0];
+}
+
+// clipRect may be null, even though we always have a clip. This indicates that
+// the path is contained in the clip, and so we can ignore it during the blit
+//
+// clipRect (if no null) has already been shifted up
+//
+void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitter,
+                  int start_y, int stop_y, int shiftEdgesUp,
+                  const SkRegion& clipRgn) {
+    SkASSERT(&path && blitter);
+
+    SkEdgeBuilder   builder;
+
+    int count = builder.build(path, clipRect, shiftEdgesUp);
+    SkEdge**    list = builder.edgeList();
+
+    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);
+        }
+
+        return;
+    }
+
+    SkEdge headEdge, tailEdge, *last;
+    // this returns the first and last edge after they're sorted into a dlink list
+    SkEdge* edge = sort_edges(list, count, &last);
+
+    headEdge.fPrev = NULL;
+    headEdge.fNext = edge;
+    headEdge.fFirstY = kEDGE_HEAD_Y;
+    headEdge.fX = SK_MinS32;
+    edge->fPrev = &headEdge;
+
+    tailEdge.fPrev = last;
+    tailEdge.fNext = NULL;
+    tailEdge.fFirstY = kEDGE_TAIL_Y;
+    last->fNext = &tailEdge;
+
+    // now edge is the head of the sorted linklist
+
+    start_y <<= shiftEdgesUp;
+    stop_y <<= shiftEdgesUp;
+    if (clipRect && start_y < clipRect->fTop) {
+        start_y = clipRect->fTop;
+    }
+    if (clipRect && stop_y > clipRect->fBottom) {
+        stop_y = clipRect->fBottom;
+    }
+
+    InverseBlitter  ib;
+    PrePostProc     proc = NULL;
+
+    if (path.isInverseFillType()) {
+        ib.setBlitter(blitter, clipRgn.getBounds(), shiftEdgesUp);
+        blitter = &ib;
+        proc = PrePostInverseBlitterProc;
+    }
+
+    if (path.isConvex() && (NULL == proc)) {
+        walk_convex_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, NULL);
+    } else {
+        walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc);
+    }
+}
+
+void sk_blit_above(SkBlitter* blitter, const SkIRect& ir, const SkRegion& clip) {
+    const SkIRect& cr = clip.getBounds();
+    SkIRect tmp;
+
+    tmp.fLeft = cr.fLeft;
+    tmp.fRight = cr.fRight;
+    tmp.fTop = cr.fTop;
+    tmp.fBottom = ir.fTop;
+    if (!tmp.isEmpty()) {
+        blitter->blitRectRegion(tmp, clip);
+    }
+}
+
+void sk_blit_below(SkBlitter* blitter, const SkIRect& ir, const SkRegion& clip) {
+    const SkIRect& cr = clip.getBounds();
+    SkIRect tmp;
+
+    tmp.fLeft = cr.fLeft;
+    tmp.fRight = cr.fRight;
+    tmp.fTop = ir.fBottom;
+    tmp.fBottom = cr.fBottom;
+    if (!tmp.isEmpty()) {
+        blitter->blitRectRegion(tmp, clip);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip,
+                             const SkIRect& ir) {
+    fBlitter = NULL;     // null means blit nothing
+    fClipRect = NULL;
+
+    if (clip) {
+        fClipRect = &clip->getBounds();
+        if (!SkIRect::Intersects(*fClipRect, ir)) { // completely clipped out
+            return;
+        }
+
+        if (clip->isRect()) {
+            if (fClipRect->contains(ir)) {
+                fClipRect = NULL;
+            } else {
+                // only need a wrapper blitter if we're horizontally clipped
+                if (fClipRect->fLeft > ir.fLeft || fClipRect->fRight < ir.fRight) {
+                    fRectBlitter.init(blitter, *fClipRect);
+                    blitter = &fRectBlitter;
+                }
+            }
+        } else {
+            fRgnBlitter.init(blitter, clip);
+            blitter = &fRgnBlitter;
+        }
+    }
+    fBlitter = blitter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool clip_to_limit(const SkRegion& orig, SkRegion* reduced) {
+    const int32_t limit = 32767;
+
+    SkIRect limitR;
+    limitR.set(-limit, -limit, limit, limit);
+    if (limitR.contains(orig.getBounds())) {
+        return false;
+    }
+    reduced->op(orig, limitR, SkRegion::kIntersect_Op);
+    return true;
+}
+
+void SkScan::FillPath(const SkPath& path, const SkRegion& origClip,
+                      SkBlitter* blitter) {
+    if (origClip.isEmpty()) {
+        return;
+    }
+
+    // Our edges are fixed-point, and don't like the bounds of the clip to
+    // exceed that. Here we trim the clip just so we don't overflow later on
+    const SkRegion* clipPtr = &origClip;
+    SkRegion finiteClip;
+    if (clip_to_limit(origClip, &finiteClip)) {
+        if (finiteClip.isEmpty()) {
+            return;
+        }
+        clipPtr = &finiteClip;
+    }
+        // don't reference "origClip" any more, just use clipPtr
+
+    SkIRect ir;
+    path.getBounds().round(&ir);
+    if (ir.isEmpty()) {
+        if (path.isInverseFillType()) {
+            blitter->blitRegion(*clipPtr);
+        }
+        return;
+    }
+
+    SkScanClipper   clipper(blitter, clipPtr, ir);
+
+    blitter = clipper.getBlitter();
+    if (blitter) {
+        // we have to keep our calls to blitter in sorted order, so we
+        // must blit the above section first, then the middle, then the bottom.
+        if (path.isInverseFillType()) {
+            sk_blit_above(blitter, ir, *clipPtr);
+        }
+        sk_fill_path(path, clipper.getClipRect(), blitter, ir.fTop, ir.fBottom,
+                     0, *clipPtr);
+        if (path.isInverseFillType()) {
+            sk_blit_below(blitter, ir, *clipPtr);
+        }
+    } else {
+        // what does it mean to not have a blitter if path.isInverseFillType???
+    }
+}
+
+void SkScan::FillPath(const SkPath& path, const SkIRect& ir,
+                      SkBlitter* blitter) {
+    SkRegion rgn(ir);
+    FillPath(path, rgn, blitter);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int build_tri_edges(SkEdge edge[], const SkPoint pts[],
+                           const SkIRect* clipRect, SkEdge* list[]) {
+    SkEdge** start = list;
+
+    if (edge->setLine(pts[0], pts[1], clipRect, 0)) {
+        *list++ = edge;
+        edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
+    }
+    if (edge->setLine(pts[1], pts[2], clipRect, 0)) {
+        *list++ = edge;
+        edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
+    }
+    if (edge->setLine(pts[2], pts[0], clipRect, 0)) {
+        *list++ = edge;
+    }
+    return (int)(list - start);
+}
+
+
+static void sk_fill_triangle(const SkPoint pts[], const SkIRect* clipRect,
+                             SkBlitter* blitter, const SkIRect& ir) {
+    SkASSERT(pts && blitter);
+
+    SkEdge edgeStorage[3];
+    SkEdge* list[3];
+
+    int count = build_tri_edges(edgeStorage, pts, clipRect, list);
+    if (count < 2) {
+        return;
+    }
+
+    SkEdge headEdge, tailEdge, *last;
+
+    // this returns the first and last edge after they're sorted into a dlink list
+    SkEdge* edge = sort_edges(list, count, &last);
+
+    headEdge.fPrev = NULL;
+    headEdge.fNext = edge;
+    headEdge.fFirstY = kEDGE_HEAD_Y;
+    headEdge.fX = SK_MinS32;
+    edge->fPrev = &headEdge;
+
+    tailEdge.fPrev = last;
+    tailEdge.fNext = NULL;
+    tailEdge.fFirstY = kEDGE_TAIL_Y;
+    last->fNext = &tailEdge;
+
+    // now edge is the head of the sorted linklist
+    int stop_y = ir.fBottom;
+    if (clipRect && stop_y > clipRect->fBottom) {
+        stop_y = clipRect->fBottom;
+    }
+    int start_y = ir.fTop;
+    if (clipRect && start_y < clipRect->fTop) {
+        start_y = clipRect->fTop;
+    }
+    walk_convex_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, start_y, stop_y, NULL);
+//    walk_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, start_y, stop_y, NULL);
+}
+
+void SkScan::FillTriangle(const SkPoint pts[], const SkRasterClip& clip,
+                          SkBlitter* blitter) {
+    if (clip.isEmpty()) {
+        return;
+    }
+
+    SkRect  r;
+    SkIRect ir;
+    r.set(pts, 3);
+    r.round(&ir);
+    if (ir.isEmpty() || !SkIRect::Intersects(ir, clip.getBounds())) {
+        return;
+    }
+
+    SkAAClipBlitterWrapper wrap;
+    const SkRegion* clipRgn;
+    if (clip.isBW()) {
+        clipRgn = &clip.bwRgn();
+    } else {
+        wrap.init(clip, blitter);
+        clipRgn = &wrap.getRgn();
+        blitter = wrap.getBlitter();
+    }
+
+    SkScanClipper clipper(blitter, clipRgn, ir);
+    blitter = clipper.getBlitter();
+    if (NULL != blitter) {
+        sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir);
+    }
+}
+
diff --git a/legacy/src/core/SkShader.cpp b/legacy/src/core/SkShader.cpp
new file mode 100644
index 0000000..ce7ff9e
--- /dev/null
+++ b/legacy/src/core/SkShader.cpp
@@ -0,0 +1,365 @@
+
+/*
+ * 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 "SkScalar.h"
+#include "SkShader.h"
+#include "SkPaint.h"
+#include "SkMallocPixelRef.h"
+
+SkShader::SkShader() : fLocalMatrix(NULL) {
+    SkDEBUGCODE(fInSession = false;)
+}
+
+SkShader::SkShader(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer), fLocalMatrix(NULL) {
+    if (buffer.readBool()) {
+        SkMatrix matrix;
+        SkReadMatrix(&buffer, &matrix);
+        setLocalMatrix(matrix);
+    }
+    SkDEBUGCODE(fInSession = false;)
+}
+
+SkShader::~SkShader() {
+    SkASSERT(!fInSession);
+    sk_free(fLocalMatrix);
+}
+
+void SkShader::beginSession() {
+    SkASSERT(!fInSession);
+    SkDEBUGCODE(fInSession = true;)
+}
+
+void SkShader::endSession() {
+    SkASSERT(fInSession);
+    SkDEBUGCODE(fInSession = false;)
+}
+
+void SkShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    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 SkShader::setContext(const SkBitmap& device,
+                          const SkPaint& paint,
+                          const SkMatrix& matrix) {
+    const SkMatrix* m = &matrix;
+    SkMatrix        total;
+
+    fDeviceConfig = SkToU8(device.getConfig());
+    fPaintAlpha = paint.getAlpha();
+    if (fLocalMatrix) {
+        total.setConcat(matrix, *fLocalMatrix);
+        m = &total;
+    }
+    if (m->invert(&fTotalInverse)) {
+        fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse);
+        return true;
+    }
+    return false;
+}
+
+#include "SkColorPriv.h"
+
+void SkShader::shadeSpan16(int x, int y, uint16_t span16[], int count) {
+    SkASSERT(span16);
+    SkASSERT(count > 0);
+    SkASSERT(this->canCallShadeSpan16());
+
+    // basically, if we get here, the subclass screwed up
+    SkDEBUGFAIL("kHasSpan16 flag is set, but shadeSpan16() not implemented");
+}
+
+#define kTempColorQuadCount 6   // balance between speed (larger) and saving stack-space
+#define kTempColorCount     (kTempColorQuadCount << 2)
+
+#ifdef SK_CPU_BENDIAN
+    #define SkU32BitShiftToByteOffset(shift)    (3 - ((shift) >> 3))
+#else
+    #define SkU32BitShiftToByteOffset(shift)    ((shift) >> 3)
+#endif
+
+void SkShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    SkASSERT(count > 0);
+
+    SkPMColor   colors[kTempColorCount];
+
+    while ((count -= kTempColorCount) >= 0) {
+        this->shadeSpan(x, y, colors, kTempColorCount);
+        x += kTempColorCount;
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        int quads = kTempColorQuadCount;
+        do {
+            U8CPU a0 = srcA[0];
+            U8CPU a1 = srcA[4];
+            U8CPU a2 = srcA[8];
+            U8CPU a3 = srcA[12];
+            srcA += 4*4;
+            *alpha++ = SkToU8(a0);
+            *alpha++ = SkToU8(a1);
+            *alpha++ = SkToU8(a2);
+            *alpha++ = SkToU8(a3);
+        } while (--quads != 0);
+    }
+    SkASSERT(count < 0);
+    SkASSERT(count + kTempColorCount >= 0);
+    if (count += kTempColorCount) {
+        this->shadeSpan(x, y, colors, count);
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        do {
+            *alpha++ = *srcA;
+            srcA += 4;
+        } while (--count != 0);
+    }
+#if 0
+    do {
+        int n = count;
+        if (n > kTempColorCount)
+            n = kTempColorCount;
+        SkASSERT(n > 0);
+
+        this->shadeSpan(x, y, colors, n);
+        x += n;
+        count -= n;
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        do {
+            *alpha++ = *srcA;
+            srcA += 4;
+        } while (--n != 0);
+    } while (count > 0);
+#endif
+}
+
+SkShader::MatrixClass SkShader::ComputeMatrixClass(const SkMatrix& mat) {
+    MatrixClass mc = kLinear_MatrixClass;
+
+    if (mat.hasPerspective()) {
+        if (mat.fixedStepInX(0, NULL, NULL)) {
+            mc = kFixedStepInX_MatrixClass;
+        } else {
+            mc = kPerspective_MatrixClass;
+        }
+    }
+    return mc;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkShader::BitmapType SkShader::asABitmap(SkBitmap*, SkMatrix*,
+                                         TileMode*, SkScalar*) const {
+    return kNone_BitmapType;
+}
+
+SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
+    return kNone_GradientType;
+}
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+                                       TileMode tmx, TileMode tmy) {
+    return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorShader.h"
+#include "SkUtils.h"
+
+SkColorShader::SkColorShader() {
+    fFlags = 0;
+    fInheritColor = true;
+}
+
+SkColorShader::SkColorShader(SkColor c) {
+    fFlags = 0;
+    fColor = c;
+    fInheritColor = false;
+}
+
+SkColorShader::~SkColorShader() {}
+
+bool SkColorShader::isOpaque() const {
+    if (fInheritColor) {
+        return true; // using paint's alpha
+    }
+    return SkColorGetA(fColor) == 255;
+}
+
+SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) {
+    fFlags = 0; // computed in setContext
+
+    fInheritColor = b.readU8();
+    if (fInheritColor) {
+        return;
+    }
+    fColor = b.readU32();
+}
+
+void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    buffer.write8(fInheritColor);
+    if (fInheritColor) {
+        return;
+    }
+    buffer.write32(fColor);
+}
+
+SkFlattenable* SkColorShader::CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkColorShader, (buffer));
+}
+
+SkFlattenable::Factory SkColorShader::getFactory() {
+    return CreateProc;
+}
+
+uint32_t SkColorShader::getFlags() {
+    return fFlags;
+}
+
+uint8_t SkColorShader::getSpan16Alpha() const {
+    return SkGetPackedA32(fPMColor);
+}
+
+bool SkColorShader::setContext(const SkBitmap& device, const SkPaint& paint,
+                               const SkMatrix& matrix) {
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    unsigned a;
+
+    if (fInheritColor) {
+        fColor = paint.getColor();
+        a = SkColorGetA(fColor);
+    } else {
+        a = SkAlphaMul(SkColorGetA(fColor), SkAlpha255To256(paint.getAlpha()));
+    }
+
+    unsigned r = SkColorGetR(fColor);
+    unsigned g = SkColorGetG(fColor);
+    unsigned b = SkColorGetB(fColor);
+
+    // we want this before we apply any alpha
+    fColor16 = SkPack888ToRGB16(r, g, b);
+
+    if (a != 255) {
+        r = SkMulDiv255Round(r, a);
+        g = SkMulDiv255Round(g, a);
+        b = SkMulDiv255Round(b, a);
+    }
+    fPMColor = SkPackARGB32(a, r, g, b);
+
+    fFlags = kConstInY32_Flag;
+    if (255 == a) {
+        fFlags |= kOpaqueAlpha_Flag;
+        if (paint.isDither() == false) {
+            fFlags |= kHasSpan16_Flag;
+        }
+    }
+
+    return true;
+}
+
+void SkColorShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
+    sk_memset32(span, fPMColor, count);
+}
+
+void SkColorShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
+    sk_memset16(span, fColor16, count);
+}
+
+void SkColorShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    memset(alpha, SkGetPackedA32(fPMColor), count);
+}
+
+// if we had a asAColor method, that would be more efficient...
+SkShader::BitmapType SkColorShader::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
+                                              TileMode modes[],
+                                      SkScalar* twoPointRadialParams) const {
+    return kNone_BitmapType;
+}
+
+SkShader::GradientType SkColorShader::asAGradient(GradientInfo* info) const {
+    if (info) {
+        if (info->fColors && info->fColorCount >= 1) {
+            info->fColors[0] = fColor;
+        }
+        info->fColorCount = 1;
+        info->fTileMode = SkShader::kRepeat_TileMode;
+    }
+    return kColor_GradientType;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkEmptyShader.h"
+
+SkEmptyShader::SkEmptyShader(SkFlattenableReadBuffer& b) : INHERITED(b) {}
+
+uint32_t SkEmptyShader::getFlags() { return 0; }
+uint8_t SkEmptyShader::getSpan16Alpha() const { return 0; }
+
+bool SkEmptyShader::setContext(const SkBitmap&, const SkPaint&,
+                               const SkMatrix&) { return false; }
+
+void SkEmptyShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
+    SkDEBUGFAIL("should never get called, since setContext() returned false");
+}
+
+void SkEmptyShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
+    SkDEBUGFAIL("should never get called, since setContext() returned false");
+}
+
+void SkEmptyShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    SkDEBUGFAIL("should never get called, since setContext() returned false");
+}
+
+SkFlattenable::Factory SkEmptyShader::getFactory() { return NULL; }
+
+void SkEmptyShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+}
+
diff --git a/src/core/SkShape.cpp b/legacy/src/core/SkShape.cpp
similarity index 100%
rename from src/core/SkShape.cpp
rename to legacy/src/core/SkShape.cpp
diff --git a/legacy/src/core/SkSinTable.h b/legacy/src/core/SkSinTable.h
new file mode 100644
index 0000000..ac26b9e
--- /dev/null
+++ b/legacy/src/core/SkSinTable.h
@@ -0,0 +1,277 @@
+
+/*
+ * 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 SkSinTable_DEFINED
+#define SkSinTable_DEFINED
+
+#include "SkTypes.h"
+
+/* Fixed point values (low 16 bits) of sin(radians) for
+    radians in [0...PI/2)
+*/
+static const uint16_t gSkSinTable[256] = {
+    0x0000,
+    0x0192,
+    0x0324,
+    0x04B6,
+    0x0648,
+    0x07DA,
+    0x096C,
+    0x0AFE,
+    0x0C8F,
+    0x0E21,
+    0x0FB2,
+    0x1144,
+    0x12D5,
+    0x1466,
+    0x15F6,
+    0x1787,
+    0x1917,
+    0x1AA7,
+    0x1C37,
+    0x1DC7,
+    0x1F56,
+    0x20E5,
+    0x2273,
+    0x2402,
+    0x2590,
+    0x271D,
+    0x28AA,
+    0x2A37,
+    0x2BC4,
+    0x2D50,
+    0x2EDB,
+    0x3066,
+    0x31F1,
+    0x337B,
+    0x3505,
+    0x368E,
+    0x3817,
+    0x399F,
+    0x3B26,
+    0x3CAD,
+    0x3E33,
+    0x3FB9,
+    0x413E,
+    0x42C3,
+    0x4447,
+    0x45CA,
+    0x474D,
+    0x48CE,
+    0x4A50,
+    0x4BD0,
+    0x4D50,
+    0x4ECF,
+    0x504D,
+    0x51CA,
+    0x5347,
+    0x54C3,
+    0x563E,
+    0x57B8,
+    0x5931,
+    0x5AAA,
+    0x5C22,
+    0x5D98,
+    0x5F0E,
+    0x6083,
+    0x61F7,
+    0x636A,
+    0x64DC,
+    0x664D,
+    0x67BD,
+    0x692D,
+    0x6A9B,
+    0x6C08,
+    0x6D74,
+    0x6EDF,
+    0x7049,
+    0x71B1,
+    0x7319,
+    0x7480,
+    0x75E5,
+    0x774A,
+    0x78AD,
+    0x7A0F,
+    0x7B70,
+    0x7CD0,
+    0x7E2E,
+    0x7F8B,
+    0x80E7,
+    0x8242,
+    0x839C,
+    0x84F4,
+    0x864B,
+    0x87A1,
+    0x88F5,
+    0x8A48,
+    0x8B9A,
+    0x8CEA,
+    0x8E39,
+    0x8F87,
+    0x90D3,
+    0x921E,
+    0x9368,
+    0x94B0,
+    0x95F6,
+    0x973C,
+    0x987F,
+    0x99C2,
+    0x9B02,
+    0x9C42,
+    0x9D7F,
+    0x9EBC,
+    0x9FF6,
+    0xA12F,
+    0xA267,
+    0xA39D,
+    0xA4D2,
+    0xA605,
+    0xA736,
+    0xA866,
+    0xA994,
+    0xAAC0,
+    0xABEB,
+    0xAD14,
+    0xAE3B,
+    0xAF61,
+    0xB085,
+    0xB1A8,
+    0xB2C8,
+    0xB3E7,
+    0xB504,
+    0xB620,
+    0xB73A,
+    0xB852,
+    0xB968,
+    0xBA7C,
+    0xBB8F,
+    0xBCA0,
+    0xBDAE,
+    0xBEBC,
+    0xBFC7,
+    0xC0D0,
+    0xC1D8,
+    0xC2DE,
+    0xC3E2,
+    0xC4E3,
+    0xC5E4,
+    0xC6E2,
+    0xC7DE,
+    0xC8D8,
+    0xC9D1,
+    0xCAC7,
+    0xCBBB,
+    0xCCAE,
+    0xCD9F,
+    0xCE8D,
+    0xCF7A,
+    0xD064,
+    0xD14D,
+    0xD233,
+    0xD318,
+    0xD3FA,
+    0xD4DB,
+    0xD5B9,
+    0xD695,
+    0xD770,
+    0xD848,
+    0xD91E,
+    0xD9F2,
+    0xDAC4,
+    0xDB94,
+    0xDC61,
+    0xDD2D,
+    0xDDF6,
+    0xDEBE,
+    0xDF83,
+    0xE046,
+    0xE106,
+    0xE1C5,
+    0xE282,
+    0xE33C,
+    0xE3F4,
+    0xE4AA,
+    0xE55E,
+    0xE60F,
+    0xE6BE,
+    0xE76B,
+    0xE816,
+    0xE8BF,
+    0xE965,
+    0xEA09,
+    0xEAAB,
+    0xEB4B,
+    0xEBE8,
+    0xEC83,
+    0xED1C,
+    0xEDB2,
+    0xEE46,
+    0xEED8,
+    0xEF68,
+    0xEFF5,
+    0xF080,
+    0xF109,
+    0xF18F,
+    0xF213,
+    0xF294,
+    0xF314,
+    0xF391,
+    0xF40B,
+    0xF484,
+    0xF4FA,
+    0xF56D,
+    0xF5DE,
+    0xF64D,
+    0xF6BA,
+    0xF724,
+    0xF78B,
+    0xF7F1,
+    0xF853,
+    0xF8B4,
+    0xF912,
+    0xF96E,
+    0xF9C7,
+    0xFA1E,
+    0xFA73,
+    0xFAC5,
+    0xFB14,
+    0xFB61,
+    0xFBAC,
+    0xFBF5,
+    0xFC3B,
+    0xFC7E,
+    0xFCBF,
+    0xFCFE,
+    0xFD3A,
+    0xFD74,
+    0xFDAB,
+    0xFDE0,
+    0xFE13,
+    0xFE43,
+    0xFE70,
+    0xFE9B,
+    0xFEC4,
+    0xFEEA,
+    0xFF0E,
+    0xFF2F,
+    0xFF4E,
+    0xFF6A,
+    0xFF84,
+    0xFF9C,
+    0xFFB1,
+    0xFFC3,
+    0xFFD3,
+    0xFFE1,
+    0xFFEC,
+    0xFFF4,
+    0xFFFB,
+    0xFFFE
+};
+
+#endif
diff --git a/legacy/src/core/SkSpriteBlitter.h b/legacy/src/core/SkSpriteBlitter.h
new file mode 100644
index 0000000..c1dbfa2
--- /dev/null
+++ b/legacy/src/core/SkSpriteBlitter.h
@@ -0,0 +1,47 @@
+
+/*
+ * 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 SkSpriteBlitter_DEFINED
+#define SkSpriteBlitter_DEFINED
+
+#include "SkBlitter.h"
+#include "SkBitmap.h"
+
+class SkPaint;
+
+class SkSpriteBlitter : public SkBlitter {
+public:
+            SkSpriteBlitter(const SkBitmap& source);
+    virtual ~SkSpriteBlitter();
+
+    virtual void setup(const SkBitmap& device, int left, int top,
+                       const SkPaint& paint);
+
+    // overrides
+#ifdef SK_DEBUG
+    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    blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void    blitMask(const SkMask&, const SkIRect& clip);
+#endif
+
+    static SkSpriteBlitter* ChooseD16(const SkBitmap& source, const SkPaint&,
+                                      void* storage, size_t storageSize);
+    static SkSpriteBlitter* ChooseD32(const SkBitmap& source, const SkPaint&,
+                                      void* storage, size_t storageSize);
+
+protected:
+    const SkBitmap* fDevice;
+    const SkBitmap* fSource;
+    int             fLeft, fTop;
+    const SkPaint*  fPaint;
+};
+
+#endif
+
diff --git a/legacy/src/core/SkSpriteBlitterTemplate.h b/legacy/src/core/SkSpriteBlitterTemplate.h
new file mode 100644
index 0000000..c43e582
--- /dev/null
+++ b/legacy/src/core/SkSpriteBlitterTemplate.h
@@ -0,0 +1,82 @@
+
+/*
+ * 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.
+ */
+
+
+
+class SkSPRITE_CLASSNAME : public SkSpriteBlitter {
+public:
+    SkSPRITE_CLASSNAME(const SkBitmap& source SkSPRITE_ARGS)
+        : SkSpriteBlitter(source) {
+        SkSPRITE_INIT
+    }
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        int srcX = x - fLeft;
+        int srcY = y - fTop;
+        SkSPRITE_DST_TYPE* SK_RESTRICT dst =fDevice->SkSPRITE_DST_GETADDR(x, y);
+        const SkSPRITE_SRC_TYPE* SK_RESTRICT src =
+                                      fSource->SkSPRITE_SRC_GETADDR(srcX, srcY);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+
+        SkDEBUGCODE((void)fDevice->SkSPRITE_DST_GETADDR(x + width - 1, y + height - 1);)
+        SkDEBUGCODE((void)fSource->SkSPRITE_SRC_GETADDR(srcX + width  - 1, srcY + height - 1);)
+
+        SkSPRITE_PREAMBLE((*fSource), srcX, srcY);
+
+        do {
+            SkSPRITE_DST_TYPE* d = dst;
+            const SkSPRITE_SRC_TYPE* s = src;
+#ifdef SkSPRITE_BEGIN_ROW
+            SkSPRITE_BEGIN_ROW
+#endif
+
+#ifdef SkSPRITE_ROW_PROC
+            SkSPRITE_ROW_PROC(d, s, width, x, y);
+#else
+            int w = width;
+            do {
+                SkSPRITE_SRC_TYPE sc = *s++;
+                SkSPRITE_BLIT_PIXEL(d, sc);
+                d += 1;
+            } while (--w != 0);
+#endif
+            dst = (SkSPRITE_DST_TYPE* SK_RESTRICT)((char*)dst + dstRB);
+            src = (const SkSPRITE_SRC_TYPE* SK_RESTRICT)
+                                            ((const char*)src + srcRB);
+            SkSPRITE_NEXT_ROW
+#ifdef SkSPRITE_ROW_PROC
+            y += 1;
+#endif
+        } while (--height != 0);
+
+        SkSPRITE_POSTAMBLE((*fSource));
+    }
+
+private:
+    SkSPRITE_FIELDS
+};
+
+#undef SkSPRITE_BLIT_PIXEL
+#undef SkSPRITE_CLASSNAME
+#undef SkSPRITE_DST_TYPE
+#undef SkSPRITE_SRC_TYPE
+#undef SkSPRITE_DST_GETADDR
+#undef SkSPRITE_SRC_GETADDR
+#undef SkSPRITE_PREAMBLE
+#undef SkSPRITE_POSTAMBLE
+#undef SkSPRITE_ARGS
+#undef SkSPRITE_FIELDS
+#undef SkSPRITE_INIT
+#undef SkSPRITE_NEXT_ROW
+#undef SkSPRITE_BEGIN_ROW
+
+#ifdef SkSPRITE_ROW_PROC
+    #undef SkSPRITE_ROW_PROC
+#endif
diff --git a/legacy/src/core/SkSpriteBlitter_ARGB32.cpp b/legacy/src/core/SkSpriteBlitter_ARGB32.cpp
new file mode 100644
index 0000000..80d3225
--- /dev/null
+++ b/legacy/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -0,0 +1,313 @@
+
+/*
+ * 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 "SkSpriteBlitter.h"
+#include "SkBlitRow.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D32_S32 : public SkSpriteBlitter {
+public:
+    Sprite_D32_S32(const SkBitmap& src, U8CPU alpha)  : INHERITED(src) {
+        SkASSERT(src.config() == SkBitmap::kARGB_8888_Config);
+
+        unsigned flags32 = 0;
+        if (255 != alpha) {
+            flags32 |= SkBlitRow::kGlobalAlpha_Flag32;
+        }
+        if (!src.isOpaque()) {
+            flags32 |= SkBlitRow::kSrcPixelAlpha_Flag32;
+        }
+
+        fProc32 = SkBlitRow::Factory32(flags32);
+        fAlpha = alpha;
+    }
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        uint32_t* SK_RESTRICT dst = fDevice->getAddr32(x, y);
+        const uint32_t* SK_RESTRICT src = fSource->getAddr32(x - fLeft,
+                                                             y - fTop);
+        size_t dstRB = fDevice->rowBytes();
+        size_t srcRB = fSource->rowBytes();
+        SkBlitRow::Proc32 proc = fProc32;
+        U8CPU             alpha = fAlpha;
+
+        do {
+            proc(dst, src, width, alpha);
+            dst = (uint32_t* SK_RESTRICT)((char*)dst + dstRB);
+            src = (const uint32_t* SK_RESTRICT)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+
+private:
+    SkBlitRow::Proc32   fProc32;
+    U8CPU               fAlpha;
+
+    typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D32_XferFilter : public SkSpriteBlitter {
+public:
+    Sprite_D32_XferFilter(const SkBitmap& source, const SkPaint& paint)
+        : SkSpriteBlitter(source) {
+        fColorFilter = paint.getColorFilter();
+        SkSafeRef(fColorFilter);
+
+        fXfermode = paint.getXfermode();
+        SkSafeRef(fXfermode);
+
+        fBufferSize = 0;
+        fBuffer = NULL;
+
+        unsigned flags32 = 0;
+        if (255 != paint.getAlpha()) {
+            flags32 |= SkBlitRow::kGlobalAlpha_Flag32;
+        }
+        if (!source.isOpaque()) {
+            flags32 |= SkBlitRow::kSrcPixelAlpha_Flag32;
+        }
+
+        fProc32 = SkBlitRow::Factory32(flags32);
+        fAlpha = paint.getAlpha();
+    }
+
+    virtual ~Sprite_D32_XferFilter() {
+        delete[] fBuffer;
+        SkSafeUnref(fXfermode);
+        SkSafeUnref(fColorFilter);
+    }
+
+    virtual void setup(const SkBitmap& device, int left, int top,
+                       const SkPaint& paint) {
+        this->INHERITED::setup(device, left, top, paint);
+
+        int width = device.width();
+        if (width > fBufferSize) {
+            fBufferSize = width;
+            delete[] fBuffer;
+            fBuffer = new SkPMColor[width];
+        }
+    }
+
+protected:
+    SkColorFilter*      fColorFilter;
+    SkXfermode*         fXfermode;
+    int                 fBufferSize;
+    SkPMColor*          fBuffer;
+    SkBlitRow::Proc32   fProc32;
+    U8CPU               fAlpha;
+
+private:
+    typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D32_S32A_XferFilter : public Sprite_D32_XferFilter {
+public:
+    Sprite_D32_S32A_XferFilter(const SkBitmap& source, const SkPaint& paint)
+        : Sprite_D32_XferFilter(source, paint) {}
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        uint32_t* SK_RESTRICT dst = fDevice->getAddr32(x, y);
+        const uint32_t* SK_RESTRICT src = fSource->getAddr32(x - fLeft,
+                                                             y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        SkColorFilter* colorFilter = fColorFilter;
+        SkXfermode* xfermode = fXfermode;
+
+        do {
+            const SkPMColor* tmp = src;
+
+            if (NULL != colorFilter) {
+                colorFilter->filterSpan(src, width, fBuffer);
+                tmp = fBuffer;
+            }
+
+            if (NULL != xfermode) {
+                xfermode->xfer32(dst, tmp, width, NULL);
+            } else {
+                fProc32(dst, tmp, width, fAlpha);
+            }
+
+            dst = (uint32_t* SK_RESTRICT)((char*)dst + dstRB);
+            src = (const uint32_t* SK_RESTRICT)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+
+private:
+    typedef Sprite_D32_XferFilter INHERITED;
+};
+
+static void fillbuffer(SkPMColor* SK_RESTRICT dst,
+                       const SkPMColor16* SK_RESTRICT src, int count) {
+    SkASSERT(count > 0);
+
+    do {
+        *dst++ = SkPixel4444ToPixel32(*src++);
+    } while (--count != 0);
+}
+
+class Sprite_D32_S4444_XferFilter : public Sprite_D32_XferFilter {
+public:
+    Sprite_D32_S4444_XferFilter(const SkBitmap& source, const SkPaint& paint)
+        : Sprite_D32_XferFilter(source, paint) {}
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SkPMColor* SK_RESTRICT dst = fDevice->getAddr32(x, y);
+        const SkPMColor16* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
+                                                                y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        SkPMColor* SK_RESTRICT buffer = fBuffer;
+        SkColorFilter* colorFilter = fColorFilter;
+        SkXfermode* xfermode = fXfermode;
+
+        do {
+            fillbuffer(buffer, src, width);
+
+            if (NULL != colorFilter) {
+                colorFilter->filterSpan(buffer, width, buffer);
+            }
+            if (NULL != xfermode) {
+                xfermode->xfer32(dst, buffer, width, NULL);
+            } else {
+                fProc32(dst, buffer, width, fAlpha);
+            }
+
+            dst = (SkPMColor* SK_RESTRICT)((char*)dst + dstRB);
+            src = (const SkPMColor16* SK_RESTRICT)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+
+private:
+    typedef Sprite_D32_XferFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void src_row(SkPMColor* SK_RESTRICT dst,
+                    const SkPMColor16* SK_RESTRICT src, int count) {
+    do {
+        *dst = SkPixel4444ToPixel32(*src);
+        src += 1;
+        dst += 1;
+    } while (--count != 0);
+}
+
+class Sprite_D32_S4444_Opaque : public SkSpriteBlitter {
+public:
+    Sprite_D32_S4444_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {}
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SkPMColor* SK_RESTRICT dst = fDevice->getAddr32(x, y);
+        const SkPMColor16* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
+                                                                y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+
+        do {
+            src_row(dst, src, width);
+            dst = (SkPMColor* SK_RESTRICT)((char*)dst + dstRB);
+            src = (const SkPMColor16* SK_RESTRICT)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+};
+
+static void srcover_row(SkPMColor* SK_RESTRICT dst,
+                        const SkPMColor16* SK_RESTRICT src, int count) {
+    do {
+        *dst = SkPMSrcOver(SkPixel4444ToPixel32(*src), *dst);
+        src += 1;
+        dst += 1;
+    } while (--count != 0);
+}
+
+class Sprite_D32_S4444 : public SkSpriteBlitter {
+public:
+    Sprite_D32_S4444(const SkBitmap& source) : SkSpriteBlitter(source) {}
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SkPMColor* SK_RESTRICT dst = fDevice->getAddr32(x, y);
+        const SkPMColor16* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
+                                                                y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+
+        do {
+            srcover_row(dst, src, width);
+            dst = (SkPMColor* SK_RESTRICT)((char*)dst + dstRB);
+            src = (const SkPMColor16* SK_RESTRICT)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source,
+                                            const SkPaint& paint,
+                                            void* storage, size_t storageSize) {
+    if (paint.getMaskFilter() != NULL) {
+        return NULL;
+    }
+
+    U8CPU       alpha = paint.getAlpha();
+    SkXfermode* xfermode = paint.getXfermode();
+    SkColorFilter* filter = paint.getColorFilter();
+    SkSpriteBlitter* blitter = NULL;
+
+    switch (source.getConfig()) {
+        case SkBitmap::kARGB_4444_Config:
+            if (alpha != 0xFF) {
+                return NULL;    // we only have opaque sprites
+            }
+            if (xfermode || filter) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_XferFilter,
+                                      storage, storageSize, (source, paint));
+            } else if (source.isOpaque()) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_Opaque,
+                                      storage, storageSize, (source));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444,
+                                      storage, storageSize, (source));
+            }
+            break;
+        case SkBitmap::kARGB_8888_Config:
+            if (xfermode || filter) {
+                if (255 == alpha) {
+                    // this can handle xfermode or filter, but not alpha
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_XferFilter,
+                                      storage, storageSize, (source, paint));
+                }
+            } else {
+                // this can handle alpha, but not xfermode or filter
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32,
+                              storage, storageSize, (source, alpha));
+            }
+            break;
+        default:
+            break;
+    }
+    return blitter;
+}
diff --git a/legacy/src/core/SkSpriteBlitter_RGB16.cpp b/legacy/src/core/SkSpriteBlitter_RGB16.cpp
new file mode 100644
index 0000000..2c38f71
--- /dev/null
+++ b/legacy/src/core/SkSpriteBlitter_RGB16.cpp
@@ -0,0 +1,377 @@
+
+/*
+ * 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 "SkSpriteBlitter.h"
+#include "SkBlitRow.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+
+#define D16_S32A_Opaque_Pixel(dst, sc)                                        \
+do {                                                                          \
+    if (sc) {                                                                 \
+        *dst = SkSrcOver32To16(sc, *dst);                                     \
+    }                                                                         \
+} while (0)
+
+static inline void D16_S32A_Blend_Pixel_helper(uint16_t* dst, SkPMColor sc,
+                                               unsigned src_scale) {
+    uint16_t dc = *dst;
+    unsigned sa = SkGetPackedA32(sc);
+    unsigned dr, dg, db;
+
+    if (255 == sa) {
+        dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale);
+        dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale);
+        db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale);
+    } else {
+        unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale);
+        dr = (SkPacked32ToR16(sc) * src_scale +
+              SkGetPackedR16(dc) * dst_scale) >> 8;
+        dg = (SkPacked32ToG16(sc) * src_scale +
+              SkGetPackedG16(dc) * dst_scale) >> 8;
+        db = (SkPacked32ToB16(sc) * src_scale +
+              SkGetPackedB16(dc) * dst_scale) >> 8;
+    }
+    *dst = SkPackRGB16(dr, dg, db);
+}
+
+#define D16_S32A_Blend_Pixel(dst, sc, src_scale) \
+    do { if (sc) D16_S32A_Blend_Pixel_helper(dst, sc, src_scale); } while (0)
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D16_S16_Opaque : public SkSpriteBlitter {
+public:
+    Sprite_D16_S16_Opaque(const SkBitmap& source)
+        : SkSpriteBlitter(source) {}
+
+    // overrides
+    virtual void blitRect(int x, int y, int width, int height) {
+        uint16_t* SK_RESTRICT dst = fDevice->getAddr16(x, y);
+        const uint16_t* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
+                                                             y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+
+        while (--height >= 0) {
+            memcpy(dst, src, width << 1);
+            dst = (uint16_t*)((char*)dst + dstRB);
+            src = (const uint16_t*)((const char*)src + srcRB);
+        }
+    }
+};
+
+#define D16_S16_Blend_Pixel(dst, sc, scale)     \
+    do {                                        \
+        uint16_t dc = *dst;                     \
+        *dst = SkBlendRGB16(sc, dc, scale);     \
+    } while (0)
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_S16_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t  fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint16_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr16
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      int scale = SkAlpha255To256(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S16_Blend_Pixel(dst, src, scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define D16_S4444_Opaque(dst, sc)           \
+    do {                                    \
+        uint16_t dc = *dst;                 \
+        *dst = SkSrcOver4444To16(sc, dc);   \
+    } while (0)
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_S4444_Opaque
+#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_BLIT_PIXEL(dst, src)       D16_S4444_Opaque(dst, src)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+#define D16_S4444_Blend(dst, sc, scale16)           \
+    do {                                            \
+        uint16_t dc = *dst;                         \
+        *dst = SkBlend4444To16(sc, dc, scale16);    \
+    } while (0)
+
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_S4444_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t  fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint16_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr16
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      int scale = SkAlpha15To16(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S4444_Blend(dst, src, scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8A_Opaque
+#define SkSPRITE_ARGS
+#define SkSPRITE_FIELDS
+#define SkSPRITE_INIT
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const SkPMColor* ctable = srcBM.getColorTable()->lockColors()
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S32A_Opaque_Pixel(dst, ctable[src])
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlockColors(false)
+#include "SkSpriteBlitterTemplate.h"
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8A_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const SkPMColor* ctable = srcBM.getColorTable()->lockColors(); unsigned src_scale = SkAlpha255To256(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S32A_Blend_Pixel(dst, ctable[src], src_scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlockColors(false);
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static intptr_t asint(const void* ptr) {
+    return reinterpret_cast<const char*>(ptr) - (const char*)0;
+}
+
+static void blitrow_d16_si8(uint16_t* SK_RESTRICT dst,
+                            const uint8_t* SK_RESTRICT src, int count,
+                            const uint16_t* SK_RESTRICT ctable) {
+    if (count <= 8) {
+        do {
+            *dst++ = ctable[*src++];
+        } while (--count);
+        return;
+    }
+
+    // eat src until we're on a 4byte boundary
+    while (asint(src) & 3) {
+        *dst++ = ctable[*src++];
+        count -= 1;
+    }
+
+    int qcount = count >> 2;
+    SkASSERT(qcount > 0);
+    const uint32_t* qsrc = reinterpret_cast<const uint32_t*>(src);
+    if (asint(dst) & 2) {
+        do {
+            uint32_t s4 = *qsrc++;
+#ifdef SK_CPU_LENDIAN
+            *dst++ = ctable[s4 & 0xFF];
+            *dst++ = ctable[(s4 >> 8) & 0xFF];
+            *dst++ = ctable[(s4 >> 16) & 0xFF];
+            *dst++ = ctable[s4 >> 24];
+#else   // BENDIAN
+            *dst++ = ctable[s4 >> 24];
+            *dst++ = ctable[(s4 >> 16) & 0xFF];
+            *dst++ = ctable[(s4 >> 8) & 0xFF];
+            *dst++ = ctable[s4 & 0xFF];
+#endif
+        } while (--qcount);
+    } else {    // dst is on a 4byte boundary
+        uint32_t* ddst = reinterpret_cast<uint32_t*>(dst);
+        do {
+            uint32_t s4 = *qsrc++;
+#ifdef SK_CPU_LENDIAN
+            *ddst++ = (ctable[(s4 >> 8) & 0xFF] << 16) | ctable[s4 & 0xFF];
+            *ddst++ = (ctable[s4 >> 24] << 16) | ctable[(s4 >> 16) & 0xFF];
+#else   // BENDIAN
+            *ddst++ = (ctable[s4 >> 24] << 16) | ctable[(s4 >> 16) & 0xFF];
+            *ddst++ = (ctable[(s4 >> 8) & 0xFF] << 16) | ctable[s4 & 0xFF];
+#endif
+        } while (--qcount);
+        dst = reinterpret_cast<uint16_t*>(ddst);
+    }
+    src = reinterpret_cast<const uint8_t*>(qsrc);
+    count &= 3;
+    // catch any remaining (will be < 4)
+    while (--count >= 0) {
+        *dst++ = ctable[*src++];
+    }
+}
+
+#define SkSPRITE_ROW_PROC(d, s, n, x, y)    blitrow_d16_si8(d, s, n, ctable)
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8_Opaque
+#define SkSPRITE_ARGS
+#define SkSPRITE_FIELDS
+#define SkSPRITE_INIT
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache()
+#define SkSPRITE_BLIT_PIXEL(dst, src)       *dst = ctable[src]
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlock16BitCache()
+#include "SkSpriteBlitterTemplate.h"
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache(); unsigned src_scale = SkAlpha255To256(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S16_Blend_Pixel(dst, ctable[src], src_scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlock16BitCache();
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D16_S32_BlitRowProc : public SkSpriteBlitter {
+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;
+        }
+        if (!fSource->isOpaque()) {
+            flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+        }
+        if (paint.isDither()) {
+            flags |= SkBlitRow::kDither_Flag;
+        }
+        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,
+                                                              y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        SkBlitRow::Proc proc = fProc;
+        U8CPU alpha = fPaint->getAlpha();
+        
+        while (--height >= 0) {
+            proc(dst, src, width, alpha, x, y);
+            y += 1;
+            dst = (uint16_t* SK_RESTRICT)((char*)dst + dstRB);
+            src = (const SkPMColor* SK_RESTRICT)((const char*)src + srcRB);
+        }
+    }
+    
+private:
+    SkBlitRow::Proc fProc;
+    
+    typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source,
+                                            const SkPaint& paint,
+                                            void* storage, size_t storageSize) {
+    if (paint.getMaskFilter() != NULL) { // may add cases for this
+        return NULL;
+    }
+    if (paint.getXfermode() != NULL) { // may add cases for this
+        return NULL;
+    }
+    if (paint.getColorFilter() != NULL) { // may add cases for this
+        return NULL;
+    }
+
+    SkSpriteBlitter* blitter = NULL;
+    unsigned alpha = paint.getAlpha();
+
+    switch (source.getConfig()) {
+        case SkBitmap::kARGB_8888_Config:
+            SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_BlitRowProc,
+                                  storage, storageSize, (source));
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            if (255 == alpha) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Opaque,
+                                      storage, storageSize, (source));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Blend,
+                                    storage, storageSize, (source, alpha >> 4));
+            }
+            break;
+        case SkBitmap::kRGB_565_Config:
+            if (255 == alpha) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Opaque,
+                                      storage, storageSize, (source));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Blend,
+                                      storage, storageSize, (source, alpha));
+            }
+            break;
+        case SkBitmap::kIndex8_Config:
+            if (paint.isDither()) {
+                // we don't support dither yet in these special cases
+                break;
+            }
+            if (source.isOpaque()) {
+                if (255 == alpha) {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Opaque,
+                                          storage, storageSize, (source));
+                } else {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Blend,
+                                         storage, storageSize, (source, alpha));
+                }
+            } else {
+                if (255 == alpha) {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Opaque,
+                                          storage, storageSize, (source));
+                } else {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Blend,
+                                         storage, storageSize, (source, alpha));
+                }
+            }
+            break;
+        default:
+            break;
+    }
+    return blitter;
+}
diff --git a/legacy/src/core/SkStream.cpp b/legacy/src/core/SkStream.cpp
new file mode 100644
index 0000000..8b71244
--- /dev/null
+++ b/legacy/src/core/SkStream.cpp
@@ -0,0 +1,750 @@
+
+/*
+ * 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 "SkStream.h"
+#include "SkData.h"
+#include "SkFixed.h"
+#include "SkString.h"
+#include "SkOSFile.h"
+
+SkStream::~SkStream() {}
+
+const char* SkStream::getFileName()
+{
+    // override in subclass if you represent a file
+    return NULL;
+}
+
+const void* SkStream::getMemoryBase()
+{
+    // override in subclass if you represent a memory block
+    return NULL;
+}
+
+size_t SkStream::skip(size_t size)
+{
+    /*  Check for size == 0, and just return 0. If we passed that
+        to read(), it would interpret it as a request for the entire
+        size of the stream.
+    */
+    return size ? this->read(NULL, size) : 0;
+}
+
+int8_t SkStream::readS8() {
+    int8_t value;
+    SkDEBUGCODE(size_t len =) this->read(&value, 1);
+    SkASSERT(1 == len);
+    return value;
+}
+
+int16_t SkStream::readS16() {
+    int16_t value;
+    SkDEBUGCODE(size_t len =) this->read(&value, 2);
+    SkASSERT(2 == len);
+    return value;
+}
+
+int32_t SkStream::readS32() {
+    int32_t value;
+    SkDEBUGCODE(size_t len =) this->read(&value, 4);
+    SkASSERT(4 == len);
+    return value;
+}
+
+SkScalar SkStream::readScalar() {
+    SkScalar value;
+    SkDEBUGCODE(size_t len =) this->read(&value, sizeof(SkScalar));
+    SkASSERT(sizeof(SkScalar) == len);
+    return value;
+}
+
+#define SK_MAX_BYTE_FOR_U8          0xFD
+#define SK_BYTE_SENTINEL_FOR_U16    0xFE
+#define SK_BYTE_SENTINEL_FOR_U32    0xFF
+
+size_t SkStream::readPackedUInt() {
+    uint8_t byte;    
+    if (!this->read(&byte, 1)) {
+        return 0;
+    }
+    if (SK_BYTE_SENTINEL_FOR_U16 == byte) {
+        return this->readU16();
+    } else if (SK_BYTE_SENTINEL_FOR_U32 == byte) {
+        return this->readU32();
+    } else {
+        return byte;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkWStream::~SkWStream()
+{
+}
+
+void SkWStream::newline()
+{
+    this->write("\n", 1);
+}
+
+void SkWStream::flush()
+{
+}
+
+bool SkWStream::writeText(const char text[])
+{
+    SkASSERT(text);
+    return this->write(text, strlen(text));
+}
+
+bool SkWStream::writeDecAsText(int32_t dec)
+{
+    SkString    tmp;
+    tmp.appendS32(dec);
+    return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::writeBigDecAsText(int64_t dec, int minDigits)
+{
+    SkString    tmp;
+    tmp.appendS64(dec, minDigits);
+    return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::writeHexAsText(uint32_t hex, int digits)
+{
+    SkString    tmp;
+    tmp.appendHex(hex, digits);
+    return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::writeScalarAsText(SkScalar value)
+{
+    SkString    tmp;
+    tmp.appendScalar(value);
+    return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::write8(U8CPU value) {
+    uint8_t v = SkToU8(value);
+    return this->write(&v, 1);
+}
+
+bool SkWStream::write16(U16CPU value) {
+    uint16_t v = SkToU16(value);
+    return this->write(&v, 2);
+}
+
+bool SkWStream::write32(uint32_t value) {
+    return this->write(&value, 4);
+}
+
+bool SkWStream::writeScalar(SkScalar value) {
+    return this->write(&value, sizeof(value));
+}
+
+bool SkWStream::writePackedUInt(size_t value) {
+    uint8_t data[5];
+    size_t len = 1;
+    if (value <= SK_MAX_BYTE_FOR_U8) {
+        data[0] = value;
+        len = 1;
+    } else if (value <= 0xFFFF) {
+        uint16_t value16 = value;
+        data[0] = SK_BYTE_SENTINEL_FOR_U16;
+        memcpy(&data[1], &value16, 2);
+        len = 3;
+    } else {
+        uint32_t value32 = value;
+        data[0] = SK_BYTE_SENTINEL_FOR_U32;
+        memcpy(&data[1], &value32, 4);
+        len = 5;
+    }
+    return this->write(data, len);
+}
+
+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) {
+            n = MAX;
+        }
+        stream->read(scratch, n);
+        if (!this->write(scratch, n)) {
+            return false;
+        }
+        length -= n;
+    }
+    return true;
+}
+
+bool SkWStream::writeData(const SkData* data) {
+    if (data) {
+        this->write(data->data(), data->size());
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFILEStream::SkFILEStream(const char file[]) : fName(file)
+{
+    fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL;
+}
+
+SkFILEStream::~SkFILEStream()
+{
+    if (fFILE)
+        sk_fclose(fFILE);
+}
+
+void SkFILEStream::setPath(const char path[])
+{
+    fName.set(path);
+    if (fFILE)
+    {
+        sk_fclose(fFILE);
+        fFILE = NULL;
+    }
+    if (path)
+        fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag);
+}
+
+const char* SkFILEStream::getFileName()
+{
+    return fName.c_str();
+}
+
+bool SkFILEStream::rewind()
+{
+    if (fFILE)
+    {
+        if (sk_frewind(fFILE))
+            return true;
+        // we hit an error
+        sk_fclose(fFILE);
+        fFILE = NULL;
+    }
+    return false;
+}
+
+size_t SkFILEStream::read(void* buffer, size_t size)
+{
+    if (fFILE)
+    {
+        if (buffer == NULL && size == 0)    // special signature, they want the total size
+            return sk_fgetsize(fFILE);
+        else
+            return sk_fread(buffer, size, fFILE);
+    }
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkData* newFromParams(const void* src, size_t size, bool copyData) {
+    if (copyData) {
+        return SkData::NewWithCopy(src, size);
+    } else {
+        return SkData::NewWithProc(src, size, NULL, NULL);
+    }
+}
+
+SkMemoryStream::SkMemoryStream() {
+    fData = SkData::NewEmpty();
+    fOffset = 0;
+}
+
+SkMemoryStream::SkMemoryStream(size_t size) {
+    fData = SkData::NewFromMalloc(sk_malloc_throw(size), size);
+    fOffset = 0;
+}
+
+SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData) {
+    fData = newFromParams(src, size, copyData);
+    fOffset = 0;
+}
+
+SkMemoryStream::~SkMemoryStream() {
+    fData->unref();
+}
+
+void SkMemoryStream::setMemoryOwned(const void* src, size_t size) {
+    fData->unref();
+    fData = SkData::NewFromMalloc(src, size);
+    fOffset = 0;
+}
+
+void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) {
+    fData->unref();
+    fData = newFromParams(src, size, copyData);
+    fOffset = 0;
+}
+
+SkData* SkMemoryStream::copyToData() const {
+    fData->ref();
+    return fData;
+}
+
+SkData* SkMemoryStream::setData(SkData* data) {
+    SkRefCnt_SafeAssign(fData, data);
+    return data;
+}
+
+void SkMemoryStream::skipToAlign4() {
+    // cast to remove unary-minus warning
+    fOffset += -(int)fOffset & 0x03;
+}
+
+bool SkMemoryStream::rewind() {
+    fOffset = 0;
+    return true;
+}
+
+size_t SkMemoryStream::read(void* buffer, size_t size) {
+    size_t dataSize = fData->size();
+
+    if (buffer == NULL && size == 0)    // special signature, they want the total size
+        return dataSize;
+
+    // if buffer is NULL, seek ahead by size
+
+    if (size == 0) {
+        return 0;
+    }
+    if (size > dataSize - fOffset) {
+        size = dataSize - fOffset;
+    }
+    if (buffer) {
+        memcpy(buffer, fData->bytes() + fOffset, size);
+    }
+    fOffset += size;
+    return size;
+}
+
+const void* SkMemoryStream::getMemoryBase() {
+    return fData->data();
+}
+
+const void* SkMemoryStream::getAtPos() {
+    return fData->bytes() + fOffset;
+}
+
+size_t SkMemoryStream::seek(size_t offset) {
+    if (offset > fData->size()) {
+        offset = fData->size();
+    }
+    fOffset = offset;
+    return offset;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBufferStream::SkBufferStream(SkStream* proxy, size_t bufferSize)
+    : fProxy(proxy)
+{
+    SkASSERT(proxy != NULL);
+    proxy->ref();
+    this->init(NULL, bufferSize);
+}
+
+SkBufferStream::SkBufferStream(SkStream* proxy, void* buffer, size_t bufferSize)
+    : fProxy(proxy)
+{
+    SkASSERT(proxy != NULL);
+    SkASSERT(buffer == NULL || bufferSize != 0);    // init(addr, 0) makes no sense, we must know how big their buffer is
+    proxy->ref();
+    this->init(buffer, bufferSize);
+}
+
+void SkBufferStream::init(void* buffer, size_t bufferSize)
+{
+    if (bufferSize == 0)
+        bufferSize = kDefaultBufferSize;
+
+    fOrigBufferSize = bufferSize;
+    fBufferSize = bufferSize;
+    fBufferOffset = bufferSize; // to trigger a reload on the first read()
+
+    if (buffer == NULL)
+    {
+        fBuffer = (char*)sk_malloc_throw(fBufferSize);
+        fWeOwnTheBuffer = true;
+    }
+    else
+    {
+        fBuffer = (char*)buffer;
+        fWeOwnTheBuffer = false;
+    }
+}
+
+SkBufferStream::~SkBufferStream()
+{
+    fProxy->unref();
+    if (fWeOwnTheBuffer)
+        sk_free(fBuffer);
+}
+
+bool SkBufferStream::rewind()
+{
+    fBufferOffset = fBufferSize = fOrigBufferSize;
+    return fProxy->rewind();
+}
+
+const char* SkBufferStream::getFileName()
+{
+    return fProxy->getFileName();
+}
+
+#ifdef SK_DEBUG
+//  #define SK_TRACE_BUFFERSTREAM
+#endif
+
+size_t SkBufferStream::read(void* buffer, size_t size) {
+#ifdef SK_TRACE_BUFFERSTREAM
+    SkDebugf("Request %d", size);
+#endif
+
+    if (buffer == NULL && size == 0) {
+        return fProxy->read(buffer, size);    // requesting total size
+    }
+
+    if (0 == size) {
+        return 0;
+    }
+
+    // skip size bytes
+    if (NULL == buffer) {
+        size_t remaining = fBufferSize - fBufferOffset;
+        if (remaining >= size) {
+            fBufferOffset += size;
+            return size;
+        }
+        // if we get here, we are being asked to skip beyond our current buffer
+        // so reset our offset to force a read next time, and skip the diff
+        // in our proxy
+        fBufferOffset = fOrigBufferSize;
+        return remaining + fProxy->read(NULL, size - remaining);
+    }
+
+    size_t s = size;
+    size_t actuallyRead = 0;
+
+    // flush what we can from our fBuffer
+    if (fBufferOffset < fBufferSize)
+    {
+        if (s > fBufferSize - fBufferOffset)
+            s = fBufferSize - fBufferOffset;
+        memcpy(buffer, fBuffer + fBufferOffset, s);
+#ifdef SK_TRACE_BUFFERSTREAM
+        SkDebugf(" flush %d", s);
+#endif
+        size -= s;
+        fBufferOffset += s;
+        buffer = (char*)buffer + s;
+        actuallyRead = s;
+    }
+
+    // check if there is more to read
+    if (size)
+    {
+        SkASSERT(fBufferOffset >= fBufferSize); // need to refill our fBuffer
+
+        if (size < fBufferSize) // lets try to read more than the request
+        {
+            s = fProxy->read(fBuffer, fBufferSize);
+#ifdef SK_TRACE_BUFFERSTREAM
+            SkDebugf(" read %d into fBuffer", s);
+#endif
+            if (size > s)   // they asked for too much
+                size = s;
+            if (size)
+            {
+                memcpy(buffer, fBuffer, size);
+                actuallyRead += size;
+#ifdef SK_TRACE_BUFFERSTREAM
+                SkDebugf(" memcpy %d into dst", size);
+#endif
+            }
+
+            fBufferOffset = size;
+            fBufferSize = s;        // record the (possibly smaller) size for the buffer
+        }
+        else    // just do a direct read
+        {
+            actuallyRead += fProxy->read(buffer, size);
+#ifdef SK_TRACE_BUFFERSTREAM
+            SkDebugf(" direct read %d", size);
+#endif
+        }
+    }
+#ifdef SK_TRACE_BUFFERSTREAM
+    SkDebugf("\n");
+#endif
+    return actuallyRead;
+}
+
+const void* SkBufferStream::getMemoryBase()
+{
+    return fProxy->getMemoryBase();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkFILEWStream::SkFILEWStream(const char path[])
+{
+    fFILE = sk_fopen(path, kWrite_SkFILE_Flag);
+}
+
+SkFILEWStream::~SkFILEWStream()
+{
+    if (fFILE)
+        sk_fclose(fFILE);
+}
+
+bool SkFILEWStream::write(const void* buffer, size_t size)
+{
+    if (fFILE == NULL)
+        return false;
+
+    if (sk_fwrite(buffer, size, fFILE) != size)
+    {
+        SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);)
+        sk_fclose(fFILE);
+        fFILE = NULL;
+        return false;
+    }
+    return true;
+}
+
+void SkFILEWStream::flush()
+{
+    if (fFILE)
+        sk_fflush(fFILE);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size)
+    : fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0)
+{
+}
+
+bool SkMemoryWStream::write(const void* buffer, size_t size)
+{
+    size = SkMin32(size, fMaxLength - fBytesWritten);
+    if (size > 0)
+    {
+        memcpy(fBuffer + fBytesWritten, buffer, size);
+        fBytesWritten += size;
+        return true;
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+#define SkDynamicMemoryWStream_MinBlockSize   256
+
+struct SkDynamicMemoryWStream::Block {
+    Block*  fNext;
+    char*   fCurr;
+    char*   fStop;
+
+    const char* start() const { return (const char*)(this + 1); }
+    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);
+        memcpy(fCurr, data, size);
+        fCurr += size;
+        return (const void*)((const char*)data + size);
+    }
+};
+
+SkDynamicMemoryWStream::SkDynamicMemoryWStream()
+    : fHead(NULL), fTail(NULL), fBytesWritten(0), fCopy(NULL)
+{
+}
+
+SkDynamicMemoryWStream::~SkDynamicMemoryWStream()
+{
+    reset();
+}
+
+void SkDynamicMemoryWStream::reset()
+{
+    this->invalidateCopy();
+    
+    Block*  block = fHead;
+    
+    while (block != NULL) {
+        Block*  next = block->fNext;
+        sk_free(block);
+        block = next;
+    }
+    fHead = fTail = NULL;
+    fBytesWritten = 0;
+}
+
+bool SkDynamicMemoryWStream::write(const void* buffer, size_t count)
+{
+    if (count > 0) {
+        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;        
+            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
+            fHead = fTail = block;
+        fTail = block;
+    }
+    return true;
+}
+
+bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count)
+{
+    if (offset + count > fBytesWritten) {
+        return false; // test does not partially modify
+    }
+
+    this->invalidateCopy();
+    
+    Block* block = fHead;
+    while (block != NULL) {
+        size_t size = block->written();
+        if (offset < size) {
+            size_t part = offset + count > size ? size - offset : count;
+            memcpy(block->start() + offset, buffer, part);
+            if (count <= part)
+                return true;
+            count -= part;
+            buffer = (const void*) ((char* ) buffer + part);
+        }
+        offset = offset > size ? offset - size : 0;
+        block = block->fNext;
+    }
+    return false;
+}
+
+bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count)
+{
+    if (offset + count > fBytesWritten)
+        return false; // test does not partially modify
+    Block* block = fHead;
+    while (block != NULL) {
+        size_t size = block->written();
+        if (offset < size) {
+            size_t part = offset + count > size ? size - offset : count;
+            memcpy(buffer, block->start() + offset, part);
+            if (count <= part)
+                return true;
+            count -= part;
+            buffer = (void*) ((char* ) buffer + part);
+        }
+        offset = offset > size ? offset - size : 0;
+        block = block->fNext;
+    }
+    return false;
+}
+
+void SkDynamicMemoryWStream::copyTo(void* dst) const
+{
+    if (fCopy) {
+        memcpy(dst, fCopy->data(), fBytesWritten);
+    } else {
+        Block* block = fHead;
+        
+        while (block != NULL) {
+            size_t size = block->written();
+            memcpy(dst, block->start(), size);
+            dst = (void*)((char*)dst + size);
+            block = block->fNext;
+        }
+    }
+}
+
+void SkDynamicMemoryWStream::padToAlign4()
+{
+    // cast to remove unary-minus warning
+    int padBytes = -(int)fBytesWritten & 0x03;
+    if (padBytes == 0)
+        return;
+    int zero = 0;
+    write(&zero, padBytes);
+}
+
+SkData* SkDynamicMemoryWStream::copyToData() const {
+    if (NULL == fCopy) {
+        void* buffer = sk_malloc_throw(fBytesWritten);
+        this->copyTo(buffer);
+        fCopy = SkData::NewFromMalloc(buffer, fBytesWritten);
+    }
+    fCopy->ref();
+    return fCopy;
+}
+
+void SkDynamicMemoryWStream::invalidateCopy() {
+    if (fCopy) {
+        fCopy->unref();
+        fCopy = NULL;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDebugWStream::newline()
+{
+#ifdef SK_DEBUG
+    SkDebugf("\n");
+#endif
+}
+
+bool SkDebugWStream::write(const void* buffer, size_t size)
+{
+#ifdef SK_DEBUG
+    char* s = new char[size+1];
+    memcpy(s, buffer, size);
+    s[size] = 0;
+    SkDebugf("%s", s);
+    delete[] s;
+#endif
+    return true;
+}
diff --git a/legacy/src/core/SkString.cpp b/legacy/src/core/SkString.cpp
new file mode 100644
index 0000000..4658ff8
--- /dev/null
+++ b/legacy/src/core/SkString.cpp
@@ -0,0 +1,613 @@
+
+/*
+ * 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 "SkString.h"
+#include "SkFixed.h"
+#include "SkThread.h"
+#include "SkUtils.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+// number of bytes (on the stack) to receive the printf result
+static const size_t kBufferSize = 256;
+
+#ifdef SK_BUILD_FOR_WIN
+    #define VSNPRINTF(buffer, size, format, args) \
+        _vsnprintf_s(buffer, size, _TRUNCATE, format, args)
+    #define SNPRINTF    _snprintf
+#else
+    #define VSNPRINTF   vsnprintf
+    #define SNPRINTF    snprintf
+#endif
+
+#define ARGS_TO_BUFFER(format, buffer, size)        \
+    do {                                            \
+        va_list args;                               \
+        va_start(args, format);                     \
+        VSNPRINTF(buffer, size, format, args);      \
+        va_end(args);                               \
+    } while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkStrStartsWith(const char string[], const char prefix[]) {
+    SkASSERT(string);
+    SkASSERT(prefix);
+    return !strncmp(string, prefix, strlen(prefix));
+}
+
+bool SkStrEndsWith(const char string[], const char suffix[]) {
+    SkASSERT(string);
+    SkASSERT(suffix);
+    size_t  strLen = strlen(string);
+    size_t  suffixLen = strlen(suffix);
+    return  strLen >= suffixLen &&
+            !strncmp(string + strLen - suffixLen, suffix, suffixLen);
+}
+
+int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
+    int index = 0;
+    do {
+        const char* limit = strchr(prefixes, '\0');
+        if (!strncmp(string, prefixes, limit - prefixes)) {
+            return index;
+        }
+        prefixes = limit + 1;
+        index++;
+    } while (prefixes[0]);
+    return -1;
+}
+
+char* SkStrAppendS32(char string[], int32_t dec) {
+    SkDEBUGCODE(char* start = string;)
+
+    char    buffer[SkStrAppendS32_MaxSize];
+    char*   p = buffer + sizeof(buffer);
+    bool    neg = false;
+
+    if (dec < 0) {
+        neg = true;
+        dec = -dec;
+    }
+
+    do {
+        *--p = SkToU8('0' + dec % 10);
+        dec /= 10;
+    } while (dec != 0);
+
+    if (neg) {
+        *--p = '-';
+    }
+
+    SkASSERT(p >= buffer);
+    char* stop = buffer + sizeof(buffer);
+    while (p < stop) {
+        *string++ = *p++;
+    }
+    SkASSERT(string - start <= SkStrAppendS32_MaxSize);
+    return string;
+}
+
+char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
+    SkDEBUGCODE(char* start = string;)
+
+    char    buffer[SkStrAppendS64_MaxSize];
+    char*   p = buffer + sizeof(buffer);
+    bool    neg = false;
+
+    if (dec < 0) {
+        neg = true;
+        dec = -dec;
+    }
+
+    do {
+        *--p = SkToU8('0' + dec % 10);
+        dec /= 10;
+        minDigits--;
+    } while (dec != 0);
+
+    while (minDigits > 0) {
+        *--p = '0';
+        minDigits--;
+    }
+
+    if (neg) {
+        *--p = '-';
+    }
+    SkASSERT(p >= buffer);
+    size_t cp_len = buffer + sizeof(buffer) - p;
+    memcpy(string, p, cp_len);
+    string += cp_len;
+
+    SkASSERT(string - start <= SkStrAppendS64_MaxSize);
+    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";
+    // make it 1 larger for the terminating 0
+    char buffer[SkStrAppendScalar_MaxSize + 1];
+    int len = SNPRINTF(buffer, sizeof(buffer), gFormat, value);
+    memcpy(string, buffer, len);
+    SkASSERT(len <= SkStrAppendScalar_MaxSize);
+    return string + len;
+}
+#endif
+
+char* SkStrAppendFixed(char string[], SkFixed x) {
+    SkDEBUGCODE(char* start = string;)
+    if (x < 0) {
+        *string++ = '-';
+        x = -x;
+    }
+
+    unsigned frac = x & 0xFFFF;
+    x >>= 16;
+    if (frac == 0xFFFF) {
+        // need to do this to "round up", since 65535/65536 is closer to 1 than to .9999
+        x += 1;
+        frac = 0;
+    }
+    string = SkStrAppendS32(string, x);
+
+    // now handle the fractional part (if any)
+    if (frac) {
+        static const uint16_t   gTens[] = { 1000, 100, 10, 1 };
+        const uint16_t*         tens = gTens;
+
+        x = SkFixedRound(frac * 10000);
+        SkASSERT(x <= 10000);
+        if (x == 10000) {
+            x -= 1;
+        }
+        *string++ = '.';
+        do {
+            unsigned powerOfTen = *tens++;
+            *string++ = SkToU8('0' + x / powerOfTen);
+            x %= powerOfTen;
+        } while (x != 0);
+    }
+
+    SkASSERT(string - start <= SkStrAppendScalar_MaxSize);
+    return string;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// the 3 values are [length] [refcnt] [terminating zero data]
+const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 };
+
+#define SizeOfRec()     (gEmptyRec.data() - (const char*)&gEmptyRec)
+
+SkString::Rec* SkString::AllocRec(const char text[], size_t len) {
+    Rec* rec;
+
+    if (0 == len) {
+        rec = const_cast<Rec*>(&gEmptyRec);
+    } else {
+        // add 1 for terminating 0, then align4 so we can have some slop when growing the string
+        rec = (Rec*)sk_malloc_throw(SizeOfRec() + SkAlign4(len + 1));
+        rec->fLength = len;
+        rec->fRefCnt = 1;
+        if (text) {
+            memcpy(rec->data(), text, len);
+        }
+        rec->data()[len] = 0;
+    }
+    return rec;
+}
+
+SkString::Rec* SkString::RefRec(Rec* src) {
+    if (src != &gEmptyRec) {
+        sk_atomic_inc(&src->fRefCnt);
+    }
+    return src;
+}
+
+#ifdef SK_DEBUG
+void SkString::validate() const {
+    // make sure know one has written over our global
+    SkASSERT(0 == gEmptyRec.fLength);
+    SkASSERT(0 == gEmptyRec.fRefCnt);
+    SkASSERT(0 == gEmptyRec.data()[0]);
+
+    if (fRec != &gEmptyRec) {
+        SkASSERT(fRec->fLength > 0);
+        SkASSERT(fRec->fRefCnt > 0);
+        SkASSERT(0 == fRec->data()[fRec->fLength]);
+    }
+    SkASSERT(fStr == c_str());
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(size_t len) {
+    SkASSERT(SkToU16(len) == len);  // can't handle larger than 64K
+
+    fRec = AllocRec(NULL, (U16CPU)len);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(const char text[]) {
+    size_t  len = text ? strlen(text) : 0;
+
+    fRec = AllocRec(text, (U16CPU)len);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(const char text[], size_t len) {
+    fRec = AllocRec(text, (U16CPU)len);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(const SkString& src) {
+    src.validate();
+
+    fRec = RefRec(src.fRec);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::~SkString() {
+    this->validate();
+
+    if (fRec->fLength) {
+        SkASSERT(fRec->fRefCnt > 0);
+        if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
+            sk_free(fRec);
+        }
+    }
+}
+
+bool SkString::equals(const SkString& src) const {
+    return fRec == src.fRec || this->equals(src.c_str(), src.size());
+}
+
+bool SkString::equals(const char text[]) const {
+    return this->equals(text, text ? strlen(text) : 0);
+}
+
+bool SkString::equals(const char text[], size_t len) const {
+    SkASSERT(len == 0 || text != NULL);
+
+    return fRec->fLength == len && !memcmp(fRec->data(), text, len);
+}
+
+SkString& SkString::operator=(const SkString& src) {
+    this->validate();
+
+    if (fRec != src.fRec) {
+        SkString    tmp(src);
+        this->swap(tmp);
+    }
+    return *this;
+}
+
+SkString& SkString::operator=(const char text[]) {
+    this->validate();
+
+    SkString tmp(text);
+    this->swap(tmp);
+
+    return *this;
+}
+
+void SkString::reset() {
+    this->validate();
+
+    if (fRec->fLength) {
+        SkASSERT(fRec->fRefCnt > 0);
+        if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
+            sk_free(fRec);
+        }
+    }
+
+    fRec = const_cast<Rec*>(&gEmptyRec);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+char* SkString::writable_str() {
+    this->validate();
+
+    if (fRec->fLength) {
+        if (fRec->fRefCnt > 1) {
+            Rec* rec = AllocRec(fRec->data(), fRec->fLength);
+            if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
+                // In this case after our check of fRecCnt > 1, we suddenly
+                // did become the only owner, so now we have two copies of the
+                // data (fRec and rec), so we need to delete one of them.
+                sk_free(fRec);
+            }
+            fRec = rec;
+        #ifdef SK_DEBUG
+            fStr = fRec->data();
+        #endif
+        }
+    }
+    return fRec->data();
+}
+
+void SkString::set(const char text[]) {
+    this->set(text, text ? strlen(text) : 0);
+}
+
+void SkString::set(const char text[], size_t len) {
+    if (0 == len) {
+        this->reset();
+    } else if (1 == fRec->fRefCnt && len <= fRec->fLength) {
+        // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
+        // just use less of the buffer without allocating a smaller one
+        char* p = this->writable_str();
+        if (text) {
+            memcpy(p, text, len);
+        }
+        p[len] = 0;
+        fRec->fLength = len;
+    } else if (1 == fRec->fRefCnt && (fRec->fLength >> 2) == (len >> 2)) {
+        // we have spare room in the current allocation, so don't alloc a larger one
+        char* p = this->writable_str();
+        if (text) {
+            memcpy(p, text, len);
+        }
+        p[len] = 0;
+        fRec->fLength = len;
+    } else {
+        SkString tmp(text, len);
+        this->swap(tmp);
+    }
+}
+
+void SkString::setUTF16(const uint16_t src[]) {
+    int count = 0;
+
+    while (src[count]) {
+        count += 1;
+    }
+    setUTF16(src, count);
+}
+
+void SkString::setUTF16(const uint16_t src[], size_t count) {
+    if (0 == count) {
+        this->reset();
+    } else if (count <= fRec->fLength) {
+        // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
+        if (count < fRec->fLength) {
+            this->resize(count);
+        }
+        char* p = this->writable_str();
+        for (size_t i = 0; i < count; i++) {
+            p[i] = SkToU8(src[i]);
+        }
+        p[count] = 0;
+    } else {
+        SkString tmp(count); // puts a null terminator at the end of the string
+        char*    p = tmp.writable_str();
+
+        for (size_t i = 0; i < count; i++) {
+            p[i] = SkToU8(src[i]);
+        }
+        this->swap(tmp);
+    }
+}
+
+void SkString::insert(size_t offset, const char text[]) {
+    this->insert(offset, text, text ? strlen(text) : 0);
+}
+
+void SkString::insert(size_t offset, const char text[], size_t len) {
+    if (len) {
+        size_t length = fRec->fLength;
+        if (offset > length) {
+            offset = length;
+        }
+
+        /*  If we're the only owner, and we have room in our allocation for the insert,
+            do it in place, rather than allocating a new buffer.
+
+            To know we have room, compare the allocated sizes
+            beforeAlloc = SkAlign4(length + 1)
+            afterAlloc  = SkAligh4(length + 1 + len)
+            but SkAlign4(x) is (x + 3) >> 2 << 2
+            which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
+            and we can then eliminate the +1+3 since that doesn't affec the answer
+        */
+        if (1 == fRec->fRefCnt && (length >> 2) == ((length + len) >> 2)) {
+            char* dst = this->writable_str();
+
+            if (offset < length) {
+                memmove(dst + offset + len, dst + offset, length - offset);
+            }
+            memcpy(dst + offset, text, len);
+
+            dst[length + len] = 0;
+            fRec->fLength = length + len;
+        } else {
+            /*  Seems we should use realloc here, since that is safe if it fails
+                (we have the original data), and might be faster than alloc/copy/free.
+            */
+            SkString    tmp(fRec->fLength + len);
+            char*       dst = tmp.writable_str();
+
+            if (offset > 0) {
+                memcpy(dst, fRec->data(), offset);
+            }
+            memcpy(dst + offset, text, len);
+            if (offset < fRec->fLength) {
+                memcpy(dst + offset + len, fRec->data() + offset,
+                       fRec->fLength - offset);
+            }
+
+            this->swap(tmp);
+        }
+    }
+}
+
+void SkString::insertUnichar(size_t offset, SkUnichar uni) {
+    char    buffer[kMaxBytesInUTF8Sequence];
+    size_t  len = SkUTF8_FromUnichar(uni, buffer);
+
+    if (len) {
+        this->insert(offset, buffer, len);
+    }
+}
+
+void SkString::insertS32(size_t offset, int32_t dec) {
+    char    buffer[SkStrAppendS32_MaxSize];
+    char*   stop = SkStrAppendS32(buffer, dec);
+    this->insert(offset, buffer, stop - buffer);
+}
+
+void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
+    char    buffer[SkStrAppendS64_MaxSize];
+    char*   stop = SkStrAppendS64(buffer, dec, minDigits);
+    this->insert(offset, buffer, stop - buffer);
+}
+
+void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
+    minDigits = SkPin32(minDigits, 0, 8);
+
+    static const char gHex[] = "0123456789ABCDEF";
+
+    char    buffer[8];
+    char*   p = buffer + sizeof(buffer);
+
+    do {
+        *--p = gHex[hex & 0xF];
+        hex >>= 4;
+        minDigits -= 1;
+    } while (hex != 0);
+
+    while (--minDigits >= 0) {
+        *--p = '0';
+    }
+
+    SkASSERT(p >= buffer);
+    this->insert(offset, p, buffer + sizeof(buffer) - p);
+}
+
+void SkString::insertScalar(size_t offset, SkScalar value) {
+    char    buffer[SkStrAppendScalar_MaxSize];
+    char*   stop = SkStrAppendScalar(buffer, value);
+    this->insert(offset, buffer, stop - buffer);
+}
+
+void SkString::printf(const char format[], ...) {
+    char    buffer[kBufferSize];
+    ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+    this->set(buffer, strlen(buffer));
+}
+
+void SkString::appendf(const char format[], ...) {
+    char    buffer[kBufferSize];
+    ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+    this->append(buffer, strlen(buffer));
+}
+
+void SkString::prependf(const char format[], ...) {
+    char    buffer[kBufferSize];
+    ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+    this->prepend(buffer, strlen(buffer));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkString::remove(size_t offset, size_t length) {
+    size_t size = this->size();
+
+    if (offset < size) {
+        if (offset + length > size) {
+            length = size - offset;
+        }
+        if (length > 0) {
+            SkASSERT(size > length);
+            SkString    tmp(size - length);
+            char*       dst = tmp.writable_str();
+            const char* src = this->c_str();
+
+            if (offset) {
+                SkASSERT(offset <= tmp.size());
+                memcpy(dst, src, offset);
+            }
+            size_t tail = size - offset - length;
+            SkASSERT((int32_t)tail >= 0);
+            if (tail) {
+        //      SkASSERT(offset + length <= tmp.size());
+                memcpy(dst + offset, src + offset + length, tail);
+            }
+            SkASSERT(dst[tmp.size()] == 0);
+            this->swap(tmp);
+        }
+    }
+}
+
+void SkString::swap(SkString& other) {
+    this->validate();
+    other.validate();
+
+    SkTSwap<Rec*>(fRec, other.fRec);
+#ifdef SK_DEBUG
+    SkTSwap<const char*>(fStr, other.fStr);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkAutoUCS2::SkAutoUCS2(const char utf8[]) {
+    size_t len = strlen(utf8);
+    fUCS2 = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
+
+    uint16_t* dst = fUCS2;
+    for (;;) {
+        SkUnichar uni = SkUTF8_NextUnichar(&utf8);
+        *dst++ = SkToU16(uni);
+        if (uni == 0) {
+            break;
+        }
+    }
+    fCount = (int)(dst - fUCS2);
+}
+
+SkAutoUCS2::~SkAutoUCS2() {
+    sk_free(fUCS2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkString SkStringPrintf(const char* format, ...) {
+    SkString formattedOutput;
+    char buffer[kBufferSize];
+    ARGS_TO_BUFFER(format, buffer, kBufferSize);
+    formattedOutput.set(buffer);
+    return formattedOutput;
+}
+
+#undef VSNPRINTF
+#undef SNPRINTF
+
diff --git a/legacy/src/core/SkStroke.cpp b/legacy/src/core/SkStroke.cpp
new file mode 100644
index 0000000..4b486b1
--- /dev/null
+++ b/legacy/src/core/SkStroke.cpp
@@ -0,0 +1,664 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkStrokerPriv.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+
+#define kMaxQuadSubdivide   5
+#define kMaxCubicSubdivide  4
+
+static inline bool degenerate_vector(const SkVector& v) {
+    return !SkPoint::CanNormalize(v.fX, v.fY);
+}
+
+static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
+    /*  root2/2 is a 45-degree angle
+        make this constant bigger for more subdivisions (but not >= 1)
+    */
+    static const SkScalar kFlatEnoughNormalDotProd =
+                                            SK_ScalarSqrt2/2 + SK_Scalar1/10;
+
+    SkASSERT(kFlatEnoughNormalDotProd > 0 &&
+             kFlatEnoughNormalDotProd < SK_Scalar1);
+
+    return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
+}
+
+static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
+    static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000;
+
+    return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd;
+}
+
+static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
+                                  SkScalar radius,
+                                  SkVector* normal, SkVector* unitNormal) {
+    if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
+        return false;
+    }
+    unitNormal->rotateCCW();
+    unitNormal->scale(radius, normal);
+    return true;
+}
+
+static bool set_normal_unitnormal(const SkVector& vec,
+                                  SkScalar radius,
+                                  SkVector* normal, SkVector* unitNormal) {
+    if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
+        return false;
+    }
+    unitNormal->rotateCCW();
+    unitNormal->scale(radius, normal);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkPathStroker {
+public:
+    SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
+                  SkPaint::Join join);
+
+    void moveTo(const SkPoint&);
+    void lineTo(const SkPoint&);
+    void quadTo(const SkPoint&, const SkPoint&);
+    void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
+    void close(bool isLine) { this->finishContour(true, isLine); }
+
+    void done(SkPath* dst, bool isLine) {
+        this->finishContour(false, isLine);
+        fOuter.addPath(fExtra);
+        dst->swap(fOuter);
+    }
+
+private:
+    SkScalar    fRadius;
+    SkScalar    fInvMiterLimit;
+
+    SkVector    fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
+    SkPoint     fFirstPt, fPrevPt;  // on original path
+    SkPoint     fFirstOuterPt;
+    int         fSegmentCount;
+    bool        fPrevIsLine;
+
+    SkStrokerPriv::CapProc  fCapper;
+    SkStrokerPriv::JoinProc fJoiner;
+
+    SkPath  fInner, fOuter; // outer is our working answer, inner is temp
+    SkPath  fExtra;         // added as extra complete contours
+
+    void    finishContour(bool close, bool isLine);
+    void    preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
+                      bool isLine);
+    void    postJoinTo(const SkPoint&, const SkVector& normal,
+                       const SkVector& unitNormal);
+
+    void    line_to(const SkPoint& currPt, const SkVector& normal);
+    void    quad_to(const SkPoint pts[3],
+                    const SkVector& normalAB, const SkVector& unitNormalAB,
+                    SkVector* normalBC, SkVector* unitNormalBC,
+                    int subDivide);
+    void    cubic_to(const SkPoint pts[4],
+                    const SkVector& normalAB, const SkVector& unitNormalAB,
+                    SkVector* normalCD, SkVector* unitNormalCD,
+                    int subDivide);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
+                              SkVector* unitNormal, bool currIsLine) {
+    SkASSERT(fSegmentCount >= 0);
+
+    SkScalar    prevX = fPrevPt.fX;
+    SkScalar    prevY = fPrevPt.fY;
+
+    SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
+                                         unitNormal));
+
+    if (fSegmentCount == 0) {
+        fFirstNormal = *normal;
+        fFirstUnitNormal = *unitNormal;
+        fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
+
+        fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
+        fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
+    } else {    // we have a previous segment
+        fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
+                fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
+    }
+    fPrevIsLine = currIsLine;
+}
+
+void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
+                               const SkVector& unitNormal) {
+    fPrevPt = currPt;
+    fPrevUnitNormal = unitNormal;
+    fPrevNormal = normal;
+    fSegmentCount += 1;
+}
+
+void SkPathStroker::finishContour(bool close, bool currIsLine) {
+    if (fSegmentCount > 0) {
+        SkPoint pt;
+
+        if (close) {
+            fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
+                    fFirstUnitNormal, fRadius, fInvMiterLimit,
+                    fPrevIsLine, currIsLine);
+            fOuter.close();
+            // now add fInner as its own contour
+            fInner.getLastPt(&pt);
+            fOuter.moveTo(pt.fX, pt.fY);
+            fOuter.reversePathTo(fInner);
+            fOuter.close();
+        } else {    // add caps to start and end
+            // cap the end
+            fInner.getLastPt(&pt);
+            fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
+                    currIsLine ? &fInner : NULL);
+            fOuter.reversePathTo(fInner);
+            // cap the start
+            fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
+                    fPrevIsLine ? &fInner : NULL);
+            fOuter.close();
+        }
+    }
+    fInner.reset();
+    fSegmentCount = -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit,
+                             SkPaint::Cap cap, SkPaint::Join join)
+        : fRadius(radius) {
+
+    /*  This is only used when join is miter_join, but we initialize it here
+        so that it is always defined, to fis valgrind warnings.
+    */
+    fInvMiterLimit = 0;
+
+    if (join == SkPaint::kMiter_Join) {
+        if (miterLimit <= SK_Scalar1) {
+            join = SkPaint::kBevel_Join;
+        } else {
+            fInvMiterLimit = SkScalarInvert(miterLimit);
+        }
+    }
+    fCapper = SkStrokerPriv::CapFactory(cap);
+    fJoiner = SkStrokerPriv::JoinFactory(join);
+    fSegmentCount = -1;
+    fPrevIsLine = false;
+}
+
+void SkPathStroker::moveTo(const SkPoint& pt) {
+    if (fSegmentCount > 0) {
+        this->finishContour(false, false);
+    }
+    fSegmentCount = 0;
+    fFirstPt = fPrevPt = pt;
+}
+
+void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
+    fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
+    fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
+}
+
+void SkPathStroker::lineTo(const SkPoint& currPt) {
+    if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
+        return;
+    }
+    SkVector    normal, unitNormal;
+
+    this->preJoinTo(currPt, &normal, &unitNormal, true);
+    this->line_to(currPt, normal);
+    this->postJoinTo(currPt, normal, unitNormal);
+}
+
+void SkPathStroker::quad_to(const SkPoint pts[3],
+                      const SkVector& normalAB, const SkVector& unitNormalAB,
+                      SkVector* normalBC, SkVector* unitNormalBC,
+                      int subDivide) {
+    if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
+                               normalBC, unitNormalBC)) {
+        // pts[1] nearly equals pts[2], so just draw a line to pts[2]
+        this->line_to(pts[2], normalAB);
+        *normalBC = normalAB;
+        *unitNormalBC = unitNormalAB;
+        return;
+    }
+
+    if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
+        SkPoint     tmp[5];
+        SkVector    norm, unit;
+
+        SkChopQuadAtHalf(pts, tmp);
+        this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
+        this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
+    } else {
+        SkVector    normalB, unitB;
+        SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
+                                             &normalB, &unitB));
+
+        fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
+                        pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
+        fInner.quadTo(  pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
+                        pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
+    }
+}
+
+void SkPathStroker::cubic_to(const SkPoint pts[4],
+                      const SkVector& normalAB, const SkVector& unitNormalAB,
+                      SkVector* normalCD, SkVector* unitNormalCD,
+                      int subDivide) {
+    SkVector    ab = pts[1] - pts[0];
+    SkVector    cd = pts[3] - pts[2];
+    SkVector    normalBC, unitNormalBC;
+
+    bool    degenerateAB = degenerate_vector(ab);
+    bool    degenerateCD = degenerate_vector(cd);
+
+    if (degenerateAB && degenerateCD) {
+DRAW_LINE:
+        this->line_to(pts[3], normalAB);
+        *normalCD = normalAB;
+        *unitNormalCD = unitNormalAB;
+        return;
+    }
+
+    if (degenerateAB) {
+        ab = pts[2] - pts[0];
+        degenerateAB = degenerate_vector(ab);
+    }
+    if (degenerateCD) {
+        cd = pts[3] - pts[1];
+        degenerateCD = degenerate_vector(cd);
+    }
+    if (degenerateAB || degenerateCD) {
+        goto DRAW_LINE;
+    }
+    SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
+    bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
+                                               &normalBC, &unitNormalBC);
+
+    if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
+             normals_too_curvy(unitNormalBC, *unitNormalCD)) {
+        // subdivide if we can
+        if (--subDivide < 0) {
+            goto DRAW_LINE;
+        }
+        SkPoint     tmp[7];
+        SkVector    norm, unit, dummy, unitDummy;
+
+        SkChopCubicAtHalf(pts, tmp);
+        this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
+                       subDivide);
+        // we use dummys since we already have a valid (and more accurate)
+        // normals for CD
+        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
+            normalB = pts[2] - pts[0];
+            normalB.rotateCCW();
+            SkAssertResult(normalB.setLength(fRadius));
+
+            normalC = pts[3] - pts[1];
+            normalC.rotateCCW();
+            SkAssertResult(normalC.setLength(fRadius));
+        } else {    // miter-join
+            SkVector    unitBC = pts[2] - pts[1];
+            unitBC.normalize();
+            unitBC.rotateCCW();
+
+            normalB = unitNormalAB + unitBC;
+            normalC = *unitNormalCD + unitBC;
+
+            SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
+            SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
+                                        SkScalarSqrt((SK_Scalar1 + dot)/2))));
+            dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
+            SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
+                                        SkScalarSqrt((SK_Scalar1 + dot)/2))));
+        }
+
+        fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
+                        pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
+                        pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
+
+        fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
+                        pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
+                        pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
+    }
+}
+
+void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
+    bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
+    bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
+
+    if (degenerateAB | degenerateBC) {
+        if (degenerateAB ^ degenerateBC) {
+            this->lineTo(pt2);
+        }
+        return;
+    }
+
+    SkVector    normalAB, unitAB, normalBC, unitBC;
+
+    this->preJoinTo(pt1, &normalAB, &unitAB, false);
+
+    {
+        SkPoint pts[3], tmp[5];
+        pts[0] = fPrevPt;
+        pts[1] = pt1;
+        pts[2] = pt2;
+
+        if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
+            unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
+            unitBC.rotateCCW();
+            if (normals_too_pinchy(unitAB, unitBC)) {
+                normalBC = unitBC;
+                normalBC.scale(fRadius);
+
+                fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
+                fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
+                fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
+
+                fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
+                fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
+                fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
+
+                fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
+                                 SkPath::kCW_Direction);
+            } else {
+                this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
+                              kMaxQuadSubdivide);
+                SkVector n = normalBC;
+                SkVector u = unitBC;
+                this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
+                              kMaxQuadSubdivide);
+            }
+        } else {
+            this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
+                          kMaxQuadSubdivide);
+        }
+    }
+
+    this->postJoinTo(pt2, normalBC, unitBC);
+}
+
+void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
+                            const SkPoint& pt3) {
+    bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
+    bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
+    bool    degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
+
+    if (degenerateAB + degenerateBC + degenerateCD >= 2) {
+        this->lineTo(pt3);
+        return;
+    }
+
+    SkVector    normalAB, unitAB, normalCD, unitCD;
+
+    // find the first tangent (which might be pt1 or pt2
+    {
+        const SkPoint*  nextPt = &pt1;
+        if (degenerateAB)
+            nextPt = &pt2;
+        this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
+    }
+
+    {
+        SkPoint pts[4], tmp[13];
+        int         i, count;
+        SkVector    n, u;
+        SkScalar    tValues[3];
+
+        pts[0] = fPrevPt;
+        pts[1] = pt1;
+        pts[2] = pt2;
+        pts[3] = pt3;
+
+#if 1
+        count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
+#else
+        count = 1;
+        memcpy(tmp, pts, 4 * sizeof(SkPoint));
+#endif
+        n = normalAB;
+        u = unitAB;
+        for (i = 0; i < count; i++) {
+            this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
+                           kMaxCubicSubdivide);
+            if (i == count - 1) {
+                break;
+            }
+            n = normalCD;
+            u = unitCD;
+
+        }
+
+        // check for too pinchy
+        for (i = 1; i < count; i++) {
+            SkPoint p;
+            SkVector    v, c;
+
+            SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c);
+
+            SkScalar    dot = SkPoint::DotProduct(c, c);
+            v.scale(SkScalarInvert(dot));
+
+            if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) {
+                fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
+            }
+        }
+
+    }
+
+    this->postJoinTo(pt3, normalCD, unitCD);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPaintDefaults.h"
+
+SkStroke::SkStroke() {
+    fWidth      = SK_Scalar1;
+    fMiterLimit = SkPaintDefaults_MiterLimit;
+    fCap        = SkPaint::kDefault_Cap;
+    fJoin       = SkPaint::kDefault_Join;
+    fDoFill     = false;
+}
+
+SkStroke::SkStroke(const SkPaint& p) {
+    fWidth      = p.getStrokeWidth();
+    fMiterLimit = p.getStrokeMiter();
+    fCap        = (uint8_t)p.getStrokeCap();
+    fJoin       = (uint8_t)p.getStrokeJoin();
+    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
+}
+
+SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
+    fWidth      = width;
+    fMiterLimit = p.getStrokeMiter();
+    fCap        = (uint8_t)p.getStrokeCap();
+    fJoin       = (uint8_t)p.getStrokeJoin();
+    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
+}
+
+void SkStroke::setWidth(SkScalar width) {
+    SkASSERT(width >= 0);
+    fWidth = width;
+}
+
+void SkStroke::setMiterLimit(SkScalar miterLimit) {
+    SkASSERT(miterLimit >= 0);
+    fMiterLimit = miterLimit;
+}
+
+void SkStroke::setCap(SkPaint::Cap cap) {
+    SkASSERT((unsigned)cap < SkPaint::kCapCount);
+    fCap = SkToU8(cap);
+}
+
+void SkStroke::setJoin(SkPaint::Join join) {
+    SkASSERT((unsigned)join < SkPaint::kJoinCount);
+    fJoin = SkToU8(join);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+    /*  return non-zero if the path is too big, and should be shrunk to avoid
+        overflows during intermediate calculations. Note that we compute the
+        bounds for this. If we had a custom callback/walker for paths, we could
+        perhaps go faster by using that, and just perform the abs | in that
+        routine
+    */
+    static int needs_to_shrink(const SkPath& path) {
+        const SkRect& r = path.getBounds();
+        SkFixed mask = SkAbs32(r.fLeft);
+        mask |= SkAbs32(r.fTop);
+        mask |= SkAbs32(r.fRight);
+        mask |= SkAbs32(r.fBottom);
+        // we need the top 3 bits clear (after abs) to avoid overflow
+        return mask >> 29;
+    }
+
+    static void identity_proc(SkPoint pts[], int count) {}
+    static void shift_down_2_proc(SkPoint pts[], int count) {
+        for (int i = 0; i < count; i++) {
+            pts->fX >>= 2;
+            pts->fY >>= 2;
+            pts += 1;
+        }
+    }
+    #define APPLY_PROC(proc, pts, count)    proc(pts, count)
+#else   // float does need any of this
+    #define APPLY_PROC(proc, pts, count)
+#endif
+
+void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
+    SkASSERT(&src != NULL && dst != NULL);
+
+    SkScalar radius = SkScalarHalf(fWidth);
+
+    dst->reset();
+    if (radius <= 0) {
+        return;
+    }
+    
+#ifdef SK_SCALAR_IS_FIXED
+    void (*proc)(SkPoint pts[], int count) = identity_proc;
+    if (needs_to_shrink(src)) {
+        proc = shift_down_2_proc;
+        radius >>= 2;
+        if (radius == 0) {
+            return;
+        }
+    }
+#endif
+
+    SkPathStroker   stroker(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) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                APPLY_PROC(proc, &pts[0], 1);
+                stroker.moveTo(pts[0]);
+                break;
+            case SkPath::kLine_Verb:
+                APPLY_PROC(proc, &pts[1], 1);
+                stroker.lineTo(pts[1]);
+                lastSegment = verb;
+                break;
+            case SkPath::kQuad_Verb:
+                APPLY_PROC(proc, &pts[1], 2);
+                stroker.quadTo(pts[1], pts[2]);
+                lastSegment = verb;
+                break;
+            case SkPath::kCubic_Verb:
+                APPLY_PROC(proc, &pts[1], 3);
+                stroker.cubicTo(pts[1], pts[2], pts[3]);
+                lastSegment = verb;
+                break;
+            case SkPath::kClose_Verb:
+                stroker.close(lastSegment == SkPath::kLine_Verb);
+                break;
+            default:
+                break;
+        }
+    }
+    stroker.done(dst, lastSegment == SkPath::kLine_Verb);
+
+#ifdef SK_SCALAR_IS_FIXED
+    // undo our previous down_shift
+    if (shift_down_2_proc == proc) {
+        // need a real shift methid on path. antialias paths could use this too
+        SkMatrix matrix;
+        matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
+        dst->transform(matrix);
+    }
+#endif
+
+    if (fDoFill) {
+        if (src.cheapIsDirection(SkPath::kCCW_Direction)) {
+            dst->reverseAddPath(src);
+        } else {
+            dst->addPath(src);
+        }
+    } else {
+        //  Seems like we can assume that a 2-point src would always result in
+        //  a convex stroke, but testing has proved otherwise.
+        //  TODO: fix the stroker to make this assumption true (without making
+        //  it slower that the work that will be done in computeConvexity())
+#if 0
+        // this test results in a non-convex stroke :(
+        static void test(SkCanvas* canvas) {
+            SkPoint pts[] = { 146.333328,  192.333328, 300.333344, 293.333344 };
+            SkPaint paint;
+            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()) {
+            dst->setIsConvex(true);
+        }
+#endif
+    }
+
+    // our answer should preserve the inverseness of the src
+    if (src.isInverseFillType()) {
+        SkASSERT(!dst->isInverseFillType());
+        dst->toggleInverseFillType();
+    }
+}
+
+void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
+                          SkPath* dst) const {
+    SkPath  tmp;
+
+    tmp.moveTo(p0);
+    tmp.lineTo(p1);
+    this->strokePath(tmp, dst);
+}
+
diff --git a/legacy/src/core/SkStrokerPriv.cpp b/legacy/src/core/SkStrokerPriv.cpp
new file mode 100644
index 0000000..85aae02
--- /dev/null
+++ b/legacy/src/core/SkStrokerPriv.cpp
@@ -0,0 +1,267 @@
+
+/*
+ * 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 "SkStrokerPriv.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+
+static void ButtCapper(SkPath* path, const SkPoint& pivot,
+                       const SkVector& normal, const SkPoint& stop,
+                       SkPath*)
+{
+    path->lineTo(stop.fX, stop.fY);
+}
+
+static void RoundCapper(SkPath* path, const SkPoint& pivot,
+                        const SkVector& normal, const SkPoint& stop,
+                        SkPath*)
+{
+    SkScalar    px = pivot.fX;
+    SkScalar    py = pivot.fY;
+    SkScalar    nx = normal.fX;
+    SkScalar    ny = normal.fY;
+    SkScalar    sx = SkScalarMul(nx, CUBIC_ARC_FACTOR);
+    SkScalar    sy = SkScalarMul(ny, CUBIC_ARC_FACTOR);
+
+    path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy),
+                  px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy,
+                  px + CWX(nx, ny), py + CWY(nx, ny));
+    path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy,
+                  px - nx + CWX(sx, sy), py - ny + CWY(sx, sy),
+                  stop.fX, stop.fY);
+}
+
+static void SquareCapper(SkPath* path, const SkPoint& pivot,
+                         const SkVector& normal, const SkPoint& stop,
+                         SkPath* otherPath)
+{
+    SkVector parallel;
+    normal.rotateCW(&parallel);
+
+    if (otherPath)
+    {
+        path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
+        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
+    }
+    else
+    {
+        path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
+        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
+        path->lineTo(stop.fX, stop.fY);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static bool is_clockwise(const SkVector& before, const SkVector& after)
+{
+    return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0;
+}
+
+enum AngleType {
+    kNearly180_AngleType,
+    kSharp_AngleType,
+    kShallow_AngleType,
+    kNearlyLine_AngleType
+};
+
+static AngleType Dot2AngleType(SkScalar dot)
+{
+// need more precise fixed normalization
+//  SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
+
+    if (dot >= 0)   // shallow or line
+        return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
+    else            // sharp or 180
+        return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
+}
+
+static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after)
+{
+#if 1
+    /*  In the degenerate case that the stroke radius is larger than our segments
+        just connecting the two inner segments may "show through" as a funny
+        diagonal. To pseudo-fix this, we go through the pivot point. This adds
+        an extra point/edge, but I can't see a cheap way to know when this is
+        not needed :(
+    */
+    inner->lineTo(pivot.fX, pivot.fY);
+#endif
+
+    inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
+}
+
+static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
+                        const SkPoint& pivot, const SkVector& afterUnitNormal,
+                        SkScalar radius, SkScalar invMiterLimit, bool, bool)
+{
+    SkVector    after;
+    afterUnitNormal.scale(radius, &after);
+
+    if (!is_clockwise(beforeUnitNormal, afterUnitNormal))
+    {
+        SkTSwap<SkPath*>(outer, inner);
+        after.negate();
+    }
+
+    outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
+    HandleInnerJoin(inner, pivot, after);
+}
+
+static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
+                        const SkPoint& pivot, const SkVector& afterUnitNormal,
+                        SkScalar radius, SkScalar invMiterLimit, bool, bool)
+{
+    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
+    AngleType   angleType = Dot2AngleType(dotProd);
+
+    if (angleType == kNearlyLine_AngleType)
+        return;
+
+    SkVector            before = beforeUnitNormal;
+    SkVector            after = afterUnitNormal;
+    SkRotationDirection dir = kCW_SkRotationDirection;
+
+    if (!is_clockwise(before, after))
+    {
+        SkTSwap<SkPath*>(outer, inner);
+        before.negate();
+        after.negate();
+        dir = kCCW_SkRotationDirection;
+    }
+
+    SkPoint     pts[kSkBuildQuadArcStorage];
+    SkMatrix    matrix;
+    matrix.setScale(radius, radius);
+    matrix.postTranslate(pivot.fX, pivot.fY);
+    int count = SkBuildQuadArc(before, after, dir, &matrix, pts);
+    SkASSERT((count & 1) == 1);
+
+    if (count > 1)
+    {
+        for (int i = 1; i < count; i += 2)
+            outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY);
+
+        after.scale(radius);
+        HandleInnerJoin(inner, pivot, after);
+    }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define kOneOverSqrt2   (0.707106781f)
+#else
+    #define kOneOverSqrt2   (46341)
+#endif
+
+static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
+                        const SkPoint& pivot, const SkVector& afterUnitNormal,
+                        SkScalar radius, SkScalar invMiterLimit,
+                        bool prevIsLine, bool currIsLine)
+{
+    // negate the dot since we're using normals instead of tangents
+    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
+    AngleType   angleType = Dot2AngleType(dotProd);
+    SkVector    before = beforeUnitNormal;
+    SkVector    after = afterUnitNormal;
+    SkVector    mid;
+    SkScalar    sinHalfAngle;
+    bool        ccw;
+
+    if (angleType == kNearlyLine_AngleType)
+        return;
+    if (angleType == kNearly180_AngleType)
+    {
+        currIsLine = false;
+        goto DO_BLUNT;
+    }
+    
+    ccw = !is_clockwise(before, after);
+    if (ccw)
+    {
+        SkTSwap<SkPath*>(outer, inner);
+        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
+        that (for speed an accuracy).
+        Note: we only need to check one normal if dot==0
+    */
+    if (0 == dotProd && invMiterLimit <= kOneOverSqrt2)
+    {
+        mid.set(SkScalarMul(before.fX + after.fX, radius),
+                SkScalarMul(before.fY + after.fY, radius));
+        goto DO_MITER;
+    }
+
+    /*  midLength = radius / sinHalfAngle
+        if (midLength > miterLimit * radius) abort
+        if (radius / sinHalf > miterLimit * radius) abort
+        if (1 / sinHalf > miterLimit) abort
+        if (1 / miterLimit > sinHalf) abort
+        My dotProd is opposite sign, since it is built from normals and not tangents
+        hence 1 + dot instead of 1 - dot in the formula
+    */
+    sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
+    if (sinHalfAngle < invMiterLimit)
+    {
+        currIsLine = false;
+        goto DO_BLUNT;
+    }
+
+    // choose the most accurate way to form the initial mid-vector
+    if (angleType == kSharp_AngleType)
+    {
+        mid.set(after.fY - before.fY, before.fX - after.fX);
+        if (ccw)
+            mid.negate();
+    }
+    else
+        mid.set(before.fX + after.fX, before.fY + after.fY);
+
+    mid.setLength(SkScalarDiv(radius, sinHalfAngle));
+DO_MITER:
+    if (prevIsLine)
+        outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
+    else
+        outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
+
+DO_BLUNT:
+    after.scale(radius);
+    if (!currIsLine)
+        outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
+    HandleInnerJoin(inner, pivot, after);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap)
+{
+    static const SkStrokerPriv::CapProc gCappers[] = {
+        ButtCapper, RoundCapper, SquareCapper
+    };
+
+    SkASSERT((unsigned)cap < SkPaint::kCapCount);
+    return gCappers[cap];
+}
+
+SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join)
+{
+    static const SkStrokerPriv::JoinProc gJoiners[] = {
+        MiterJoiner, RoundJoiner, BluntJoiner
+    };
+
+    SkASSERT((unsigned)join < SkPaint::kJoinCount);
+    return gJoiners[join];
+}
+
+
+
diff --git a/legacy/src/core/SkStrokerPriv.h b/legacy/src/core/SkStrokerPriv.h
new file mode 100644
index 0000000..e67d677
--- /dev/null
+++ b/legacy/src/core/SkStrokerPriv.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 SkStrokerPriv_DEFINED
+#define SkStrokerPriv_DEFINED
+
+#include "SkStroke.h"
+
+#define CWX(x, y)   (-y)
+#define CWY(x, y)   (x)
+#define CCWX(x, y)  (y)
+#define CCWY(x, y)  (-x)
+
+#define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
+
+class SkStrokerPriv {
+public:
+    typedef void (*CapProc)(SkPath* path,
+                            const SkPoint& pivot,
+                            const SkVector& normal,
+                            const SkPoint& stop,
+                            SkPath* otherPath);
+
+    typedef void (*JoinProc)(SkPath* outer, SkPath* inner,
+                             const SkVector& beforeUnitNormal,
+                             const SkPoint& pivot,
+                             const SkVector& afterUnitNormal,
+                             SkScalar radius, SkScalar invMiterLimit,
+                             bool prevIsLine, bool currIsLine);
+
+    static CapProc  CapFactory(SkPaint::Cap);
+    static JoinProc JoinFactory(SkPaint::Join);
+};
+
+#endif
+
diff --git a/legacy/src/core/SkTSearch.cpp b/legacy/src/core/SkTSearch.cpp
new file mode 100644
index 0000000..ccdcecb
--- /dev/null
+++ b/legacy/src/core/SkTSearch.cpp
@@ -0,0 +1,179 @@
+
+/*
+ * 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 "SkTSearch.h"
+#include <ctype.h>
+
+static inline const char* index_into_base(const char*const* base, int index,
+                                          size_t elemSize)
+{
+    return *(const char*const*)((const char*)base + index * elemSize);
+}
+
+int SkStrSearch(const char*const* base, int count, const char target[],
+                size_t target_len, size_t elemSize)
+{
+    if (count <= 0)
+        return ~0;
+
+    SkASSERT(base != NULL);
+
+    int lo = 0;
+    int hi = count - 1;
+
+    while (lo < hi)
+    {
+        int mid = (hi + lo) >> 1;
+        const char* elem = index_into_base(base, mid, elemSize);
+
+        int cmp = strncmp(elem, target, target_len);
+        if (cmp < 0)
+            lo = mid + 1;
+        else if (cmp > 0 || strlen(elem) > target_len)
+            hi = mid;
+        else
+            return mid;
+    }
+
+    const char* elem = index_into_base(base, hi, elemSize);
+    int cmp = strncmp(elem, target, target_len);
+    if (cmp || strlen(elem) > target_len)
+    {
+        if (cmp < 0)
+            hi += 1;
+        hi = ~hi;
+    }
+    return hi;
+}
+
+int SkStrSearch(const char*const* base, int count, const char target[],
+                size_t elemSize)
+{
+    return SkStrSearch(base, count, target, strlen(target), elemSize);
+}
+
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t len, size_t elemSize)
+{
+    SkASSERT(target);
+
+    SkAutoAsciiToLC tolc(target, len);
+
+    return SkStrSearch(base, count, tolc.lc(), len, elemSize);
+}
+
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t elemSize)
+{
+    return SkStrLCSearch(base, count, target, strlen(target), elemSize);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkAutoAsciiToLC::SkAutoAsciiToLC(const char str[], size_t len)
+{
+    // see if we need to compute the length
+    if ((long)len < 0) {
+        len = strlen(str);
+    }
+    fLength = len;
+
+    // assign lc to our preallocated storage if len is small enough, or allocate
+    // it on the heap
+    char*   lc;
+    if (len <= STORAGE) {
+        lc = fStorage;
+    } else {
+        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) {
+        int c = str[i];
+        if ((c & 0x80) == 0) {   // is just ascii
+            c = tolower(c);
+        }
+        lc[i] = c;
+    }
+    lc[len] = 0;
+}
+
+SkAutoAsciiToLC::~SkAutoAsciiToLC()
+{
+    if (fLC != fStorage) {
+        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/legacy/src/core/SkTSort.h b/legacy/src/core/SkTSort.h
new file mode 100644
index 0000000..38f8f22
--- /dev/null
+++ b/legacy/src/core/SkTSort.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 SkTSort_DEFINED
+#define SkTSort_DEFINED
+
+#include "SkTypes.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;
+        }
+        if (array[root] < array[child]) {
+            SkTSwap<T>(array[root], array[child]);
+            root = child;
+        } else {
+            break;
+        }
+    }
+}
+
+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);
+    }
+    for (i = count - 1; i > 0; --i) {
+        SkTSwap<T>(array[0], array[i]);
+        SkTHeapSort_SiftDown<T>(array, 0, i-1);
+    }
+}
+
+#endif
diff --git a/legacy/src/core/SkTemplatesPriv.h b/legacy/src/core/SkTemplatesPriv.h
new file mode 100644
index 0000000..79ae609
--- /dev/null
+++ b/legacy/src/core/SkTemplatesPriv.h
@@ -0,0 +1,76 @@
+
+/*
+ * 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 SkTemplatesPriv_DEFINED
+#define SkTemplatesPriv_DEFINED
+
+#include "SkTemplates.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_BUILD_FOR_WIN32
+    #define SK_PLACEMENT_NEW(result, classname, storage, storageSize)   \
+        result = SkNEW(classname)
+
+    #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storageSize, args)    \
+        result = SkNEW_ARGS(classname, args)
+#else
+    #include <new>
+    #define SK_PLACEMENT_NEW(result, classname, storage, storagesize)       \
+    do {                                                                    \
+        if (storagesize)                                                    \
+        {                                                                   \
+            SkASSERT(storageSize >= sizeof(classname));                     \
+            result = new(storage) classname;                                \
+        }                                                                   \
+        else                                                                \
+            result = SkNEW(classname);                                      \
+    } while (0)
+
+    #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storagesize, args)        \
+    do {                                                                                \
+        if (storagesize)                                                                \
+        {                                                                               \
+            SkASSERT(storageSize >= sizeof(classname));                                 \
+            result = new(storage) classname args;                                       \
+        }                                                                               \
+        else                                                                            \
+            result = SkNEW_ARGS(classname, args);                                       \
+    } while (0)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T> class SkAutoTPlacementDelete {
+public:
+    SkAutoTPlacementDelete(T* obj, void* storage) : fObj(obj), fStorage(storage)
+    {
+    }
+    ~SkAutoTPlacementDelete()
+    {
+        if (fObj)
+        {
+            if (fObj == fStorage)
+                fObj->~T();
+            else
+                delete fObj;
+        }
+    }
+    T* detach()
+    {
+        T*  obj = fObj;
+        fObj = NULL;
+        return obj;
+    }
+private:
+    T*      fObj;
+    void*   fStorage;
+};
+
+#endif
diff --git a/legacy/src/core/SkTextFormatParams.h b/legacy/src/core/SkTextFormatParams.h
new file mode 100644
index 0000000..dac4aef
--- /dev/null
+++ b/legacy/src/core/SkTextFormatParams.h
@@ -0,0 +1,41 @@
+
+/*
+ * 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 SkTextFormatParams_DEFINES
+#define SkTextFormatParams_DEFINES
+
+#include "SkScalar.h"
+#include "SkTypes.h"
+
+// Fraction of the text size to lower a strike through line below the baseline.
+#define kStdStrikeThru_Offset       (-SK_Scalar1 * 6 / 21)
+// Fraction of the text size to lower a underline below the baseline.
+#define kStdUnderline_Offset        (SK_Scalar1 / 9)
+// Fraction of the text size to use for a strike through or under-line.
+#define kStdUnderline_Thickness     (SK_Scalar1 / 18)
+
+// The fraction of text size to embolden fake bold text scales with text size.
+// At 9 points or below, the stroke width is increased by text size / 24.
+// At 36 points and above, it is increased by text size / 32.  In between,
+// it is interpolated between those values.
+static const SkScalar kStdFakeBoldInterpKeys[] = {
+    SK_Scalar1*9,
+    SK_Scalar1*36,
+};
+static const SkScalar kStdFakeBoldInterpValues[] = {
+    SK_Scalar1/24,
+    SK_Scalar1/32
+};
+SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kStdFakeBoldInterpKeys) ==
+                  SK_ARRAY_COUNT(kStdFakeBoldInterpValues),
+                  mismatched_array_size);
+static const int kStdFakeBoldInterpLength =
+    SK_ARRAY_COUNT(kStdFakeBoldInterpKeys);
+
+#endif  //SkTextFormatParams_DEFINES
diff --git a/legacy/src/core/SkTypeface.cpp b/legacy/src/core/SkTypeface.cpp
new file mode 100644
index 0000000..e3b49e6
--- /dev/null
+++ b/legacy/src/core/SkTypeface.cpp
@@ -0,0 +1,103 @@
+
+/*
+ * 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 "SkAdvancedTypefaceMetrics.h"
+#include "SkTypeface.h"
+#include "SkFontHost.h"
+
+//#define TRACE_LIFECYCLE
+
+#ifdef TRACE_LIFECYCLE
+    static int32_t gTypefaceCounter;
+#endif
+
+SkTypeface::SkTypeface(Style style, SkFontID fontID, bool isFixedWidth)
+    : fUniqueID(fontID), fStyle(style), fIsFixedWidth(isFixedWidth) {
+#ifdef TRACE_LIFECYCLE
+    SkDebugf("SkTypeface: create  %p fontID %d total %d\n",
+             this, fontID, ++gTypefaceCounter);
+#endif
+}
+
+SkTypeface::~SkTypeface() {
+#ifdef TRACE_LIFECYCLE
+    SkDebugf("SkTypeface: destroy %p fontID %d total %d\n",
+             this, fUniqueID, --gTypefaceCounter);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkTypeface* get_default_typeface() {
+    // 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.
+    static SkTypeface* gDefaultTypeface;
+
+    if (NULL == gDefaultTypeface) {
+        gDefaultTypeface =
+        SkFontHost::CreateTypeface(NULL, NULL, NULL, 0,
+                                   SkTypeface::kNormal);
+    }
+    return gDefaultTypeface;
+}
+
+uint32_t SkTypeface::UniqueID(const SkTypeface* face) {
+    if (NULL == face) {
+        face = get_default_typeface();
+    }
+    return face->uniqueID();
+}
+
+bool SkTypeface::Equal(const SkTypeface* facea, const SkTypeface* faceb) {
+    return SkTypeface::UniqueID(facea) == SkTypeface::UniqueID(faceb);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
+SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s) {
+    return SkFontHost::CreateTypeface(family, NULL, NULL, 0, s);
+}
+
+SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) {
+    return SkFontHost::CreateTypefaceFromStream(stream);
+}
+
+SkTypeface* SkTypeface::CreateFromFile(const char path[]) {
+    return SkFontHost::CreateTypefaceFromFile(path);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTypeface::serialize(SkWStream* stream) const {
+    SkFontHost::Serialize(this, stream);
+}
+
+SkTypeface* SkTypeface::Deserialize(SkStream* stream) {
+    return SkFontHost::Deserialize(stream);
+}
+
+SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics(
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) const {
+    return SkFontHost::GetAdvancedTypefaceMetrics(fUniqueID,
+                                                  perGlyphInfo,
+                                                  glyphIDs,
+                                                  glyphIDsCount);
+}
diff --git a/legacy/src/core/SkTypefaceCache.cpp b/legacy/src/core/SkTypefaceCache.cpp
new file mode 100644
index 0000000..f4397a6
--- /dev/null
+++ b/legacy/src/core/SkTypefaceCache.cpp
@@ -0,0 +1,125 @@
+
+/*
+ * 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 "SkTypefaceCache.h"
+#include "SkThread.h"
+
+#define TYPEFACE_CACHE_LIMIT    128
+
+void SkTypefaceCache::add(SkTypeface* face, SkTypeface::Style requestedStyle) {
+    if (fArray.count() >= TYPEFACE_CACHE_LIMIT) {
+        this->purge(TYPEFACE_CACHE_LIMIT >> 2);
+    }
+
+    Rec* rec = fArray.append();
+    rec->fFace = face;
+    rec->fRequestedStyle = requestedStyle;
+    face->ref();
+}
+
+SkTypeface* SkTypefaceCache::findByID(SkFontID fontID) const {
+    const Rec* curr = fArray.begin();
+    const Rec* stop = fArray.end();
+    while (curr < stop) {
+        if (curr->fFace->uniqueID() == fontID) {
+            return curr->fFace;
+        }
+        curr += 1;
+    }
+    return NULL;
+}
+
+SkTypeface* SkTypefaceCache::findByProc(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;
+        }
+        curr += 1;
+    }
+    return NULL;
+}
+
+void SkTypefaceCache::purge(int numToPurge) {
+    int count = fArray.count();
+    int i = 0;
+    while (i < count) {
+        SkTypeface* face = fArray[i].fFace;
+        if (1 == face->getRefCnt()) {
+            face->unref();
+            fArray.remove(i);
+            --count;
+            if (--numToPurge == 0) {
+                return;
+            }
+        } else {
+            ++i;
+        }
+    }
+}
+
+void SkTypefaceCache::purgeAll() {
+    this->purge(fArray.count());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypefaceCache& SkTypefaceCache::Get() {
+    static SkTypefaceCache gCache;
+    return gCache;
+}
+
+SkFontID SkTypefaceCache::NewFontID() {
+    static int32_t gFontID;
+    return sk_atomic_inc(&gFontID) + 1;
+}
+
+SK_DECLARE_STATIC_MUTEX(gMutex);
+
+void SkTypefaceCache::Add(SkTypeface* face, SkTypeface::Style requestedStyle) {
+    SkAutoMutexAcquire ama(gMutex);
+    Get().add(face, requestedStyle);
+}
+
+SkTypeface* SkTypefaceCache::FindByID(SkFontID fontID) {
+    SkAutoMutexAcquire ama(gMutex);
+    return Get().findByID(fontID);
+}
+
+SkTypeface* SkTypefaceCache::FindByProcAndRef(FindProc proc, void* ctx) {
+    SkAutoMutexAcquire ama(gMutex);
+    SkTypeface* typeface = Get().findByProc(proc, ctx);
+    SkSafeRef(typeface);
+    return typeface;
+}
+
+void SkTypefaceCache::PurgeAll() {
+    SkAutoMutexAcquire ama(gMutex);
+    Get().purgeAll();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+static bool DumpProc(SkTypeface* face, SkTypeface::Style style, void* ctx) {
+    SkDebugf("SkTypefaceCache: face %p fontID %d style %d refcnt %d\n",
+             face, face->uniqueID(), style, face->getRefCnt());
+    return false;
+}
+#endif
+
+void SkTypefaceCache::Dump() {
+#ifdef SK_DEBUG
+    SkAutoMutexAcquire ama(gMutex);
+    (void)Get().findByProc(DumpProc, NULL);
+#endif
+}
+
diff --git a/legacy/src/core/SkTypefaceCache.h b/legacy/src/core/SkTypefaceCache.h
new file mode 100644
index 0000000..e65ec90
--- /dev/null
+++ b/legacy/src/core/SkTypefaceCache.h
@@ -0,0 +1,91 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef SkTypefaceCache_DEFINED
+#define SkTypefaceCache_DEFINED
+
+#include "SkTypeface.h"
+#include "SkTDArray.h"
+
+/*  TODO
+ *  Provide std way to cache name+requestedStyle aliases to the same typeface.
+ *
+ *  The current mechanism ends up create a diff typeface for each one, even if
+ *  they map to the same internal obj (e.g. CTFontRef on the mac)
+ */
+
+class SkTypefaceCache {
+public:
+    /**
+     * 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.
+     */
+    typedef bool (*FindProc)(SkTypeface*, SkTypeface::Style, void* context);
+
+    /**
+     *  Helper: returns a unique fontID to pass to the constructor of
+     *  your subclass of SkTypeface
+     */
+    static SkFontID NewFontID();
+
+    /**
+     *  Add a typeface to the cache. This ref()s the typeface, so that the
+     *  cache is also an owner. Later, if we need to purge the cache, typefaces
+     *  whose refcnt is 1 (meaning only the cache is an owner) will be
+     *  unref()ed.
+     */
+    static void Add(SkTypeface*, SkTypeface::Style requested);
+
+    /**
+     *  Search the cache for a typeface with the specified fontID (uniqueID).
+     *  If one is found, return it (its reference count is unmodified). If none
+     *  is found, return NULL. The reference count is unmodified as it is
+     *  assumed that the stack will contain a ref to the typeface.
+     */
+    static SkTypeface* FindByID(SkFontID fontID);
+
+    /**
+     *  Iterate through the cache, calling proc(typeface, ctx) with each
+     *  typeface. If proc returns true, then we return that typeface (this
+     *  ref()s the typeface). If it never returns true, we return NULL.
+     */
+    static SkTypeface* FindByProcAndRef(FindProc proc, void* ctx);
+
+    /**
+     *  This will unref all of the typefaces in the cache for which the cache
+     *  is the only owner. Normally this is handled automatically as needed.
+     *  This function is exposed for clients that explicitly want to purge the
+     *  cache (e.g. to look for leaks).
+     */
+    static void PurgeAll();
+
+    /**
+     *  Debugging only: dumps the status of the typefaces in the cache
+     */
+    static void Dump();
+
+private:
+    static SkTypefaceCache& Get();
+
+    void add(SkTypeface*, SkTypeface::Style requested);
+    SkTypeface* findByID(SkFontID findID) const;
+    SkTypeface* findByProc(FindProc proc, void* ctx) const;
+    void purge(int count);
+    void purgeAll();
+
+    struct Rec {
+        SkTypeface*         fFace;
+        SkTypeface::Style   fRequestedStyle;
+    };
+    SkTDArray<Rec> fArray;
+};
+
+#endif
diff --git a/legacy/src/core/SkUnPreMultiply.cpp b/legacy/src/core/SkUnPreMultiply.cpp
new file mode 100644
index 0000000..5f32da6
--- /dev/null
+++ b/legacy/src/core/SkUnPreMultiply.cpp
@@ -0,0 +1,80 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkUnPreMultiply.h"
+#include "SkColorPriv.h"
+
+SkColor SkUnPreMultiply::PMColorToColor(SkPMColor c) {
+    const unsigned a = SkGetPackedA32(c);
+    const Scale scale = GetScale(a);
+    return SkColorSetARGB(a,
+                          ApplyScale(scale, SkGetPackedR32(c)),
+                          ApplyScale(scale, SkGetPackedG32(c)),
+                          ApplyScale(scale, SkGetPackedB32(c)));
+}
+
+const uint32_t SkUnPreMultiply::gTable[] = {
+    0x00000000, 0xFF000000, 0x7F800000, 0x55000000, 0x3FC00000, 0x33000000, 0x2A800000, 0x246DB6DB,
+    0x1FE00000, 0x1C555555, 0x19800000, 0x172E8BA3, 0x15400000, 0x139D89D9, 0x1236DB6E, 0x11000000,
+    0x0FF00000, 0x0F000000, 0x0E2AAAAB, 0x0D6BCA1B, 0x0CC00000, 0x0C249249, 0x0B9745D1, 0x0B1642C8,
+    0x0AA00000, 0x0A333333, 0x09CEC4EC, 0x0971C71C, 0x091B6DB7, 0x08CB08D4, 0x08800000, 0x0839CE74,
+    0x07F80000, 0x07BA2E8C, 0x07800000, 0x07492492, 0x07155555, 0x06E45307, 0x06B5E50D, 0x0689D89E,
+    0x06600000, 0x063831F4, 0x06124925, 0x05EE23B9, 0x05CBA2E9, 0x05AAAAAB, 0x058B2164, 0x056CEFA9,
+    0x05500000, 0x05343EB2, 0x0519999A, 0x05000000, 0x04E76276, 0x04CFB2B8, 0x04B8E38E, 0x04A2E8BA,
+    0x048DB6DB, 0x0479435E, 0x0465846A, 0x045270D0, 0x04400000, 0x042E29F8, 0x041CE73A, 0x040C30C3,
+    0x03FC0000, 0x03EC4EC5, 0x03DD1746, 0x03CE540F, 0x03C00000, 0x03B21643, 0x03A49249, 0x03976FC6,
+    0x038AAAAB, 0x037E3F20, 0x03722983, 0x03666666, 0x035AF287, 0x034FCACE, 0x0344EC4F, 0x033A5441,
+    0x03300000, 0x0325ED09, 0x031C18FA, 0x0312818B, 0x03092492, 0x03000000, 0x02F711DC, 0x02EE5847,
+    0x02E5D174, 0x02DD7BAF, 0x02D55555, 0x02CD5CD6, 0x02C590B2, 0x02BDEF7C, 0x02B677D4, 0x02AF286C,
+    0x02A80000, 0x02A0FD5C, 0x029A1F59, 0x029364D9, 0x028CCCCD, 0x0286562E, 0x02800000, 0x0279C952,
+    0x0273B13B, 0x026DB6DB, 0x0267D95C, 0x026217ED, 0x025C71C7, 0x0256E62A, 0x0251745D, 0x024C1BAD,
+    0x0246DB6E, 0x0241B2F9, 0x023CA1AF, 0x0237A6F5, 0x0232C235, 0x022DF2DF, 0x02293868, 0x02249249,
+    0x02200000, 0x021B810F, 0x021714FC, 0x0212BB51, 0x020E739D, 0x020A3D71, 0x02061862, 0x02020408,
+    0x01FE0000, 0x01FA0BE8, 0x01F62762, 0x01F25214, 0x01EE8BA3, 0x01EAD3BB, 0x01E72A08, 0x01E38E39,
+    0x01E00000, 0x01DC7F11, 0x01D90B21, 0x01D5A3EA, 0x01D24925, 0x01CEFA8E, 0x01CBB7E3, 0x01C880E5,
+    0x01C55555, 0x01C234F7, 0x01BF1F90, 0x01BC14E6, 0x01B914C2, 0x01B61EED, 0x01B33333, 0x01B05161,
+    0x01AD7943, 0x01AAAAAB, 0x01A7E567, 0x01A5294A, 0x01A27627, 0x019FCBD2, 0x019D2A20, 0x019A90E8,
+    0x01980000, 0x01957741, 0x0192F685, 0x01907DA5, 0x018E0C7D, 0x018BA2E9, 0x018940C5, 0x0186E5F1,
+    0x01849249, 0x018245AE, 0x01800000, 0x017DC11F, 0x017B88EE, 0x0179574E, 0x01772C23, 0x01750750,
+    0x0172E8BA, 0x0170D045, 0x016EBDD8, 0x016CB157, 0x016AAAAB, 0x0168A9B9, 0x0166AE6B, 0x0164B8A8,
+    0x0162C859, 0x0160DD68, 0x015EF7BE, 0x015D1746, 0x015B3BEA, 0x01596596, 0x01579436, 0x0155C7B5,
+    0x01540000, 0x01523D04, 0x01507EAE, 0x014EC4EC, 0x014D0FAC, 0x014B5EDD, 0x0149B26D, 0x01480A4B,
+    0x01466666, 0x0144C6B0, 0x01432B17, 0x0141938C, 0x01400000, 0x013E7064, 0x013CE4A9, 0x013B5CC1,
+    0x0139D89E, 0x01385831, 0x0136DB6E, 0x01356246, 0x0133ECAE, 0x01327A97, 0x01310BF6, 0x012FA0BF,
+    0x012E38E4, 0x012CD45A, 0x012B7315, 0x012A150B, 0x0128BA2F, 0x01276276, 0x01260DD6, 0x0124BC45,
+    0x01236DB7, 0x01222222, 0x0120D97D, 0x011F93BC, 0x011E50D8, 0x011D10C5, 0x011BD37A, 0x011A98EF,
+    0x0119611A, 0x01182BF3, 0x0116F970, 0x0115C988, 0x01149C34, 0x0113716B, 0x01124925, 0x01112359,
+    0x01100000, 0x010EDF12, 0x010DC087, 0x010CA458, 0x010B8A7E, 0x010A72F0, 0x01095DA9, 0x01084AA0,
+    0x010739CE, 0x01062B2E, 0x01051EB8, 0x01041466, 0x01030C31, 0x01020612, 0x01010204, 0x01000000
+};
+
+#ifdef BUILD_DIVIDE_TABLE
+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;
+            uint32_t div = roundf(j * 255.0f / i);
+            int diff = SkAbs32(test - div);
+            SkASSERT(diff <= 1 && test <= 255);
+        }
+    }
+}
+#endif
diff --git a/legacy/src/core/SkUtils.cpp b/legacy/src/core/SkUtils.cpp
new file mode 100644
index 0000000..3f1c65e
--- /dev/null
+++ b/legacy/src/core/SkUtils.cpp
@@ -0,0 +1,431 @@
+
+/*
+ * 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 "SkUtils.h"
+
+#if 0
+#define assign_16_longs(dst, value)             \
+    do {                                        \
+        (dst)[0] = value;   (dst)[1] = value;   \
+        (dst)[2] = value;   (dst)[3] = value;   \
+        (dst)[4] = value;   (dst)[5] = value;   \
+        (dst)[6] = value;   (dst)[7] = value;   \
+        (dst)[8] = value;   (dst)[9] = value;   \
+        (dst)[10] = value;  (dst)[11] = value;  \
+        (dst)[12] = value;  (dst)[13] = value;  \
+        (dst)[14] = value;  (dst)[15] = value;  \
+    } while (0)
+#else
+#define assign_16_longs(dst, value)             \
+    do {                                        \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+    } while (0)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void sk_memset16_portable(uint16_t dst[], uint16_t value, int count) {
+    SkASSERT(dst != NULL && count >= 0);
+
+    if (count <= 0) {
+        return;
+    }
+
+    // not sure if this helps to short-circuit on small values of count
+    if (count < 8) {
+        do {
+            *dst++ = (uint16_t)value;
+        } while (--count != 0);
+        return;
+    }
+
+    // ensure we're on a long boundary
+    if ((size_t)dst & 2) {
+        *dst++ = (uint16_t)value;
+        count -= 1;
+    }
+
+    uint32_t value32 = ((uint32_t)value << 16) | value;
+
+    // handle the bulk with our unrolled macro
+    {
+        int sixteenlongs = count >> 5;
+        if (sixteenlongs) {
+            uint32_t* dst32 = (uint32_t*)dst;
+            do {
+                assign_16_longs(dst32, value32);
+            } while (--sixteenlongs != 0);
+            dst = (uint16_t*)dst32;
+            count &= 31;
+        }
+    }
+
+    // handle (most) of the rest
+    {
+        int longs = count >> 1;
+        if (longs) {
+            do {
+                *(uint32_t*)dst = value32;
+                dst += 2;
+            } while (--longs != 0);
+        }
+    }
+
+    // cleanup a possible trailing short
+    if (count & 1) {
+        *dst = (uint16_t)value;
+    }
+}
+
+void sk_memset32_portable(uint32_t dst[], uint32_t value, int count) {
+    SkASSERT(dst != NULL && count >= 0);
+
+    int sixteenlongs = count >> 4;
+    if (sixteenlongs) {
+        do {
+            assign_16_longs(dst, value);
+        } while (--sixteenlongs != 0);
+        count &= 15;
+    }
+
+    if (count) {
+        do {
+            *dst++ = value;
+        } while (--count != 0);
+    }
+}
+
+static void sk_memset16_stub(uint16_t dst[], uint16_t value, int count) {
+    SkMemset16Proc proc = SkMemset16GetPlatformProc();
+    sk_memset16 = proc ? proc : sk_memset16_portable;
+    sk_memset16(dst, value, count);
+}
+
+SkMemset16Proc sk_memset16 = sk_memset16_stub;
+
+static void sk_memset32_stub(uint32_t dst[], uint32_t value, int count) {
+    SkMemset32Proc proc = SkMemset32GetPlatformProc();
+    sk_memset32 = proc ? proc : sk_memset32_portable;
+    sk_memset32(dst, value, count);
+}
+
+SkMemset32Proc sk_memset32 = sk_memset32_stub;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  0xxxxxxx    1 total
+    10xxxxxx    // never a leading byte
+    110xxxxx    2 total
+    1110xxxx    3 total
+    11110xxx    4 total
+
+    11 10 01 01 xx xx xx xx 0...
+    0xE5XX0000
+    0xE5 << 24
+*/
+
+#ifdef SK_DEBUG
+    static void assert_utf8_leadingbyte(unsigned c) {
+        SkASSERT(c <= 0xF7);    // otherwise leading byte is too big (more than 4 bytes)
+        SkASSERT((c & 0xC0) != 0x80);   // can't begin with a middle char
+    }
+
+    int SkUTF8_LeadByteToCount(unsigned c) {
+        assert_utf8_leadingbyte(c);
+        return (((0xE5 << 24) >> (c >> 4 << 1)) & 3) + 1;
+    }
+#else
+    #define assert_utf8_leadingbyte(c)
+#endif
+
+int SkUTF8_CountUnichars(const char utf8[]) {
+    SkASSERT(utf8);
+
+    int count = 0;
+
+    for (;;) {
+        int c = *(const uint8_t*)utf8;
+        if (c == 0) {
+            break;
+        }
+        utf8 += SkUTF8_LeadByteToCount(c);
+        count += 1;
+    }
+    return count;
+}
+
+int SkUTF8_CountUnichars(const char utf8[], size_t byteLength) {
+    SkASSERT(NULL != utf8 || 0 == byteLength);
+
+    int         count = 0;
+    const char* stop = utf8 + byteLength;
+
+    while (utf8 < stop) {
+        utf8 += SkUTF8_LeadByteToCount(*(const uint8_t*)utf8);
+        count += 1;
+    }
+    return count;
+}
+
+SkUnichar SkUTF8_ToUnichar(const char utf8[]) {
+    SkASSERT(NULL != utf8);
+
+    const uint8_t*  p = (const uint8_t*)utf8;
+    int             c = *p;
+    int             hic = c << 24;
+
+    assert_utf8_leadingbyte(c);
+
+    if (hic < 0) {
+        uint32_t mask = (uint32_t)~0x3F;
+        hic <<= 1;
+        do {
+            c = (c << 6) | (*++p & 0x3F);
+            mask <<= 5;
+        } while ((hic <<= 1) < 0);
+        c &= ~mask;
+    }
+    return c;
+}
+
+SkUnichar SkUTF8_NextUnichar(const char** ptr) {
+    SkASSERT(NULL != ptr && NULL != *ptr);
+
+    const uint8_t*  p = (const uint8_t*)*ptr;
+    int             c = *p;
+    int             hic = c << 24;
+    
+    assert_utf8_leadingbyte(c);
+
+    if (hic < 0) {
+        uint32_t mask = (uint32_t)~0x3F;
+        hic <<= 1;
+        do {
+            c = (c << 6) | (*++p & 0x3F);
+            mask <<= 5;
+        } while ((hic <<= 1) < 0);
+        c &= ~mask;
+    }
+    *ptr = (char*)p + 1;
+    return c;
+}
+
+SkUnichar SkUTF8_PrevUnichar(const char** ptr) {
+    SkASSERT(NULL != ptr && NULL != *ptr);
+
+    const char* p = *ptr;
+    
+    if (*--p & 0x80) {
+        while (*--p & 0x40) {
+            ;
+        }
+    }
+
+    *ptr = (char*)p;
+    return SkUTF8_NextUnichar(&p);
+}
+
+size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[]) {
+    if ((uint32_t)uni > 0x10FFFF) {
+        SkDEBUGFAIL("bad unichar");
+        return 0;
+    }
+
+    if (uni <= 127) {
+        if (utf8) {
+            *utf8 = (char)uni;
+        }
+        return 1;
+    }
+
+    char    tmp[4];
+    char*   p = tmp;
+    size_t  count = 1;
+
+    SkDEBUGCODE(SkUnichar orig = uni;)
+
+    while (uni > 0x7F >> count) {
+        *p++ = (char)(0x80 | (uni & 0x3F));
+        uni >>= 6;
+        count += 1;
+    }
+
+    if (utf8) {
+        p = tmp;
+        utf8 += count;
+        while (p < tmp + count - 1) {
+            *--utf8 = *p++;
+        }
+        *--utf8 = (char)(~(0xFF >> count) | uni);
+    }
+
+    SkASSERT(utf8 == NULL || orig == SkUTF8_ToUnichar(utf8));
+    return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkUTF16_CountUnichars(const uint16_t src[]) {
+    SkASSERT(src);
+
+    int count = 0;
+    unsigned c;
+    while ((c = *src++) != 0) {
+        SkASSERT(!SkUTF16_IsLowSurrogate(c));
+        if (SkUTF16_IsHighSurrogate(c)) {
+            c = *src++;
+            SkASSERT(SkUTF16_IsLowSurrogate(c));
+        }
+        count += 1;
+    }
+    return count;
+}
+
+int SkUTF16_CountUnichars(const uint16_t src[], int numberOf16BitValues) {
+    SkASSERT(src);
+
+    const uint16_t* stop = src + numberOf16BitValues;
+    int count = 0;
+    while (src < stop) {
+        unsigned c = *src++;
+        SkASSERT(!SkUTF16_IsLowSurrogate(c));
+        if (SkUTF16_IsHighSurrogate(c)) {
+            SkASSERT(src < stop);
+            c = *src++;
+            SkASSERT(SkUTF16_IsLowSurrogate(c));
+        }
+        count += 1;
+    }
+    return count;
+}
+
+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);
+    }
+    *srcPtr = src;
+    return c;
+}
+
+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;
+        SkASSERT(SkUTF16_IsHighSurrogate(c2));
+        c = (c2 << 10) + c + (0x10000 - (0xD800 << 10) - 0xDC00);
+    }
+    *srcPtr = src;
+    return c;
+}
+
+size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t dst[]) {
+    SkASSERT((unsigned)uni <= 0x10FFFF);
+
+    int extra = (uni > 0xFFFF);
+
+    if (dst) {
+        if (extra) {
+            // dst[0] = SkToU16(0xD800 | ((uni - 0x10000) >> 10));
+            // 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 {
+            dst[0] = SkToU16(uni);
+            SkASSERT(!SkUTF16_IsHighSurrogate(dst[0]));
+            SkASSERT(!SkUTF16_IsLowSurrogate(dst[0]));
+        }
+    }
+    return 1 + extra;
+}
+
+size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues,
+                      char utf8[]) {
+    SkASSERT(numberOf16BitValues >= 0);
+    if (numberOf16BitValues <= 0) {
+        return 0;
+    }
+
+    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);
+        }
+    } else {
+        char* start = utf8;
+        while (utf16 < stop) {
+            utf8 += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), utf8);
+        }
+        size = utf8 - start;
+    }
+    return size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+
+#if 0
+static int round_to_K(size_t bytes) {
+    return (bytes + 512) >> 10;
+}
+#endif
+
+SkAutoMemoryUsageProbe::SkAutoMemoryUsageProbe(const char label[])
+    : fLabel(label) {
+#if 0
+    struct mallinfo mi = mallinfo();
+
+    fBytesAllocated = mi.uordblks;
+#endif
+}
+
+SkAutoMemoryUsageProbe::~SkAutoMemoryUsageProbe() {
+#if 0
+    struct mallinfo mi = mallinfo();
+
+    printf("SkAutoMemoryUsageProbe ");
+    if (fLabel) {
+        printf("<%s> ", fLabel);
+    }
+    printf("delta %dK, current total allocated %dK\n",
+            round_to_K(mi.uordblks - fBytesAllocated),
+            round_to_K(mi.uordblks));
+#endif
+}
+
diff --git a/legacy/src/core/SkWriter32.cpp b/legacy/src/core/SkWriter32.cpp
new file mode 100644
index 0000000..dfa18a0
--- /dev/null
+++ b/legacy/src/core/SkWriter32.cpp
@@ -0,0 +1,252 @@
+
+/*
+ * 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;
+    }
+
+    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;
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkWriter32::~SkWriter32() {
+    this->reset();
+}
+
+void SkWriter32::reset() {
+    Block* block = fHead;    
+    while (block) {
+        Block* next = block->fNext;
+        sk_free(block);
+        block = next;
+    }
+
+    fSize = 0;
+    fHead = fTail = NULL;
+    fSingleBlock = NULL;
+}
+
+void SkWriter32::reset(void* block, size_t size) {
+    this->reset();
+    SkASSERT(0 == ((fSingleBlock - (char*)0) & 3));   // need 4-byte alignment
+    fSingleBlock = (char*)block;
+    fSingleBlockSize = (size & ~3);
+}
+
+uint32_t* SkWriter32::reserve(size_t size) {
+    SkASSERT(SkAlign4(size) == size);
+
+    if (fSingleBlock) {
+        uint32_t* ptr = (uint32_t*)(fSingleBlock + fSize);
+        fSize += size;
+        SkASSERT(fSize <= fSingleBlockSize);
+        return ptr;
+    }
+
+    Block* block = fTail;
+
+    if (NULL == block) {
+        SkASSERT(NULL == fHead);
+        fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
+    } else if (block->available() < size) {
+        fTail = Block::Create(SkMax32(size, fMinSize));
+        block->fNext = fTail;
+        block = fTail;
+    }
+    
+    fSize += size;
+
+    return block->alloc(size);
+}
+
+uint32_t* SkWriter32::peek32(size_t offset) {
+    SkASSERT(SkAlign4(offset) == offset);
+    SkASSERT(offset <= fSize);
+
+    if (fSingleBlock) {
+        return (uint32_t*)(fSingleBlock + offset);
+    }
+
+    Block* block = fHead;
+    SkASSERT(NULL != block);
+    
+    while (offset >= block->fAllocated) {
+        offset -= block->fAllocated;
+        block = block->fNext;
+        SkASSERT(NULL != block);
+    }
+    return block->peek32(offset);
+}
+
+void SkWriter32::flatten(void* dst) const {
+    if (fSingleBlock) {
+        memcpy(dst, fSingleBlock, fSize);
+        return;
+    }
+
+    const Block* block = fHead;
+    SkDEBUGCODE(size_t total = 0;)
+
+    while (block) {
+        size_t allocated = block->fAllocated;
+        memcpy(dst, block->base(), allocated);
+        dst = (char*)dst + allocated;
+        block = block->fNext;
+
+        SkDEBUGCODE(total += allocated;)
+        SkASSERT(total <= fSize);
+    }
+    SkASSERT(total == fSize);
+}
+
+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;
+    }
+}
+
+#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) {
+            n = MAX;
+        }
+        size_t bytes = stream->read(scratch, n);
+        this->writePad(scratch, bytes);
+        remaining -= bytes;
+        if (bytes != n) {
+            break;
+        }
+    }
+    return length - remaining;
+}
+
+bool SkWriter32::writeToStream(SkWStream* stream) {
+    if (fSingleBlock) {
+        return stream->write(fSingleBlock, fSize);
+    }
+
+    const Block* block = fHead;    
+    while (block) {
+        if (!stream->write(block->base(), block->fAllocated)) {
+            return false;
+        }
+        block = block->fNext;
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkReader32.h"
+#include "SkString.h"
+
+/*
+ *  Strings are stored as: length[4-bytes] + string_data + '\0' + pad_to_mul_4
+ */
+
+const char* SkReader32::readString(size_t* outLen) {
+    size_t len = this->readInt();
+    const void* ptr = this->peek();
+
+    // skip over teh string + '\0' and then pad to a multiple of 4
+    size_t alignedSize = SkAlign4(len + 1);
+    this->skip(alignedSize);
+
+    if (outLen) {
+        *outLen = len;
+    }
+    return (const char*)ptr;
+}
+
+size_t SkReader32::readIntoString(SkString* copy) {
+    size_t len;
+    const char* ptr = this->readString(&len);
+    if (copy) {
+        copy->set(ptr, len);
+    }
+    return len;
+}
+
+void SkWriter32::writeString(const char str[], size_t len) {
+    if ((long)len < 0) {
+        SkASSERT(str);
+        len = strlen(str);
+    }
+    this->write32(len);
+    // add 1 since we also write a terminating 0
+    size_t alignedLen = SkAlign4(len + 1);
+    char* ptr = (char*)this->reserve(alignedLen);
+    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) {
+    if ((long)len < 0) {
+        SkASSERT(str);
+        len = strlen(str);
+    }
+    const size_t lenBytes = 4;    // we use 4 bytes to record the length
+    // add 1 since we also write a terminating 0
+    return SkAlign4(lenBytes + len + 1);
+}
+
+
diff --git a/legacy/src/core/SkXfermode.cpp b/legacy/src/core/SkXfermode.cpp
new file mode 100644
index 0000000..efedda4
--- /dev/null
+++ b/legacy/src/core/SkXfermode.cpp
@@ -0,0 +1,1224 @@
+
+/*
+ * 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 "SkXfermode.h"
+#include "SkColorPriv.h"
+
+#define SkAlphaMulAlpha(a, b)   SkMulDiv255Round(a, b)
+
+#if 0
+// idea for higher precision blends in xfer procs (and slightly faster)
+// see DstATop as a probable caller
+static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
+    SkASSERT(a <= 255);
+    SkASSERT(b <= 255);
+    SkASSERT(c <= 255);
+    SkASSERT(d <= 255);
+    unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
+    unsigned result = (prod + (prod >> 8)) >> 8;
+    SkASSERT(result <= 255);
+    return result;
+}
+#endif
+
+static inline unsigned saturated_add(unsigned a, unsigned b) {
+    SkASSERT(a <= 255);
+    SkASSERT(b <= 255);
+    unsigned sum = a + b;
+    if (sum > 255) {
+        sum = 255;
+    }
+    return sum;
+}
+
+static inline int clamp_signed_byte(int n) {
+    if (n < 0) {
+        n = 0;
+    } else if (n > 255) {
+        n = 255;
+    }
+    return n;
+}
+
+static inline int clamp_div255round(int prod) {
+    if (prod <= 0) {
+        return 0;
+    } else if (prod >= 255*255) {
+        return 255;
+    } else {
+        return SkDiv255Round(prod);
+    }
+}
+
+static inline int clamp_max(int value, int max) {
+    if (value > max) {
+        value = max;
+    }
+    return value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+//  kClear_Mode,    //!< [0, 0]
+static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
+    return 0;
+}
+
+//  kSrc_Mode,      //!< [Sa, Sc]
+static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) {
+    return src;
+}
+
+//  kDst_Mode,      //!< [Da, Dc]
+static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) {
+    return dst;
+}
+
+//  kSrcOver_Mode,  //!< [Sa + Da - Sa*Da, Sc + (1 - Sa)*Dc]
+static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) {
+#if 0
+    // this is the old, more-correct way, but it doesn't guarantee that dst==255
+    // will always stay opaque
+    return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
+#else
+    // this is slightly faster, but more importantly guarantees that dst==255
+    // will always stay opaque
+    return src + SkAlphaMulQ(dst, 256 - SkGetPackedA32(src));
+#endif
+}
+
+//  kDstOver_Mode,  //!< [Sa + Da - Sa*Da, Dc + (1 - Da)*Sc]
+static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) {
+    // this is the reverse of srcover, just flipping src and dst
+    // see srcover's comment about the 256 for opaqueness guarantees
+    return dst + SkAlphaMulQ(src, 256 - SkGetPackedA32(dst));
+}
+
+//  kSrcIn_Mode,    //!< [Sa * Da, Sc * Da]
+static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst)));
+}
+
+//  kDstIn_Mode,    //!< [Sa * Da, Sa * Dc]
+static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src)));
+}
+
+//  kSrcOut_Mode,   //!< [Sa * (1 - Da), Sc * (1 - Da)]
+static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst)));
+}
+
+//  kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
+}
+
+//  kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned isa = 255 - sa;
+
+    return SkPackARGB32(da,
+                        SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
+                        SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
+                        SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
+}
+
+//  kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned ida = 255 - da;
+
+    return SkPackARGB32(sa,
+                        SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
+                            SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
+                            SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
+                            SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
+}
+
+//  kXor_Mode   [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned isa = 255 - sa;
+    unsigned ida = 255 - da;
+
+    return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
+                        SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// kPlus_Mode
+static SkPMColor plus_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned b = saturated_add(SkGetPackedB32(src), SkGetPackedB32(dst));
+    unsigned g = saturated_add(SkGetPackedG32(src), SkGetPackedG32(dst));
+    unsigned r = saturated_add(SkGetPackedR32(src), SkGetPackedR32(dst));
+    unsigned a = saturated_add(SkGetPackedA32(src), SkGetPackedA32(dst));
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kMultiply_Mode
+static SkPMColor multiply_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));
+    int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kScreen_Mode
+static inline int srcover_byte(int a, int b) {
+    return a + b - SkAlphaMulAlpha(a, b);
+}
+static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
+    int a = srcover_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
+    int r = srcover_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
+    int g = srcover_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
+    int b = srcover_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kOverlay_Mode
+static inline int overlay_byte(int sc, int dc, int sa, int da) {
+    int tmp = sc * (255 - da) + dc * (255 - sa);
+    int rc;
+    if (2 * dc <= da) {
+        rc = 2 * sc * dc;
+    } else {
+        rc = sa * da - 2 * (da - dc) * (sa - sc);
+    }
+    return clamp_div255round(rc + tmp);
+}
+static SkPMColor overlay_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = overlay_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = overlay_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = overlay_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kDarken_Mode
+static inline int darken_byte(int sc, int dc, int sa, int da) {
+    int sd = sc * da;
+    int ds = dc * sa;
+    if (sd < ds) {
+        // srcover
+        return sc + dc - SkDiv255Round(ds);
+    } else {
+        // dstover
+        return dc + sc - SkDiv255Round(sd);
+    }
+}
+static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = darken_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = darken_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = darken_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kLighten_Mode
+static inline int lighten_byte(int sc, int dc, int sa, int da) {
+    int sd = sc * da;
+    int ds = dc * sa;
+    if (sd > ds) {
+        // srcover
+        return sc + dc - SkDiv255Round(ds);
+    } else {
+        // dstover
+        return dc + sc - SkDiv255Round(sd);
+    }
+}
+static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = lighten_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = lighten_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = lighten_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kColorDodge_Mode
+static inline int colordodge_byte(int sc, int dc, int sa, int da) {
+    int diff = sa - sc;
+    int rc;
+    if (0 == diff) {
+        rc = sa * da + sc * (255 - da) + dc * (255 - sa);
+        rc = SkDiv255Round(rc);
+    } else {
+        int tmp = (dc * sa << 15) / (da * diff);
+        rc = SkDiv255Round(sa * da) * tmp >> 15;
+        // don't clamp here, since we'll do it in our modeproc
+    }
+    return rc;
+}
+static SkPMColor colordodge_modeproc(SkPMColor src, SkPMColor dst) {
+    // added to avoid div-by-zero in colordodge_byte
+    if (0 == dst) {
+        return src;
+    }
+
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = colordodge_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = colordodge_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = colordodge_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    r = clamp_max(r, a);
+    g = clamp_max(g, a);
+    b = clamp_max(b, a);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kColorBurn_Mode
+static inline int colorburn_byte(int sc, int dc, int sa, int da) {
+    int rc;
+    if (dc == da && 0 == sc) {
+        rc = sa * da + dc * (255 - sa);
+    } else if (0 == sc) {
+        return SkAlphaMulAlpha(dc, 255 - sa);
+    } else {
+        int tmp = (sa * (da - dc) * 256) / (sc * da);
+        if (tmp > 256) {
+            tmp = 256;
+        }
+        int tmp2 = sa * da;
+        rc = tmp2 - (tmp2 * tmp >> 8) + sc * (255 - da) + dc * (255 - sa);
+    }
+    return SkDiv255Round(rc);
+}
+static SkPMColor colorburn_modeproc(SkPMColor src, SkPMColor dst) {
+    // added to avoid div-by-zero in colorburn_byte
+    if (0 == dst) {
+        return src;
+    }
+
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = colorburn_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = colorburn_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = colorburn_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kHardLight_Mode
+static inline int hardlight_byte(int sc, int dc, int sa, int da) {
+    int rc;
+    if (2 * sc <= sa) {
+        rc = 2 * sc * dc;
+    } else {
+        rc = sa * da - 2 * (da - dc) * (sa - sc);
+    }
+    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
+}
+static SkPMColor hardlight_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = hardlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = hardlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = hardlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// returns 255 * sqrt(n/255)
+static U8CPU sqrt_unit_byte(U8CPU n) {
+    return SkSqrtBits(n, 15+4);
+}
+
+// kSoftLight_Mode
+static inline int softlight_byte(int sc, int dc, int sa, int da) {
+    int m = da ? dc * 256 / da : 0;
+    int rc;
+    if (2 * sc <= sa) {
+        rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8));
+    } else if (4 * dc <= da) {
+        int tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m;
+        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
+    } else {
+        int tmp = sqrt_unit_byte(m) - m;
+        rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8);
+    }
+    return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa));
+}
+static SkPMColor softlight_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = softlight_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = softlight_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = softlight_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kDifference_Mode
+static inline int difference_byte(int sc, int dc, int sa, int da) {
+    int tmp = SkMin32(sc * da, dc * sa);
+    return clamp_signed_byte(sc + dc - 2 * SkDiv255Round(tmp));
+}
+static SkPMColor difference_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = difference_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = difference_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = difference_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+// kExclusion_Mode
+static inline int exclusion_byte(int sc, int dc, int sa, int da) {
+    // this equations is wacky, wait for SVG to confirm it
+    int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa);
+    return clamp_div255round(r);
+}
+static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) {
+    int sa = SkGetPackedA32(src);
+    int da = SkGetPackedA32(dst);
+    int a = srcover_byte(sa, da);
+    int r = exclusion_byte(SkGetPackedR32(src), SkGetPackedR32(dst), sa, da);
+    int g = exclusion_byte(SkGetPackedG32(src), SkGetPackedG32(dst), sa, da);
+    int b = exclusion_byte(SkGetPackedB32(src), SkGetPackedB32(dst), sa, da);
+    return SkPackARGB32(a, r, g, b);
+}
+
+struct ProcCoeff {
+    SkXfermodeProc      fProc;
+    SkXfermode::Coeff   fSC;
+    SkXfermode::Coeff   fDC;
+};
+
+#define CANNOT_USE_COEFF    SkXfermode::Coeff(-1)
+
+static const ProcCoeff gProcCoeffs[] = {
+    { clear_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kZero_Coeff },
+    { src_modeproc,     SkXfermode::kOne_Coeff,     SkXfermode::kZero_Coeff },
+    { dst_modeproc,     SkXfermode::kZero_Coeff,    SkXfermode::kOne_Coeff },
+    { srcover_modeproc, SkXfermode::kOne_Coeff,     SkXfermode::kISA_Coeff },
+    { dstover_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kOne_Coeff },
+    { srcin_modeproc,   SkXfermode::kDA_Coeff,      SkXfermode::kZero_Coeff },
+    { dstin_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kSA_Coeff },
+    { srcout_modeproc,  SkXfermode::kIDA_Coeff,     SkXfermode::kZero_Coeff },
+    { dstout_modeproc,  SkXfermode::kZero_Coeff,    SkXfermode::kISA_Coeff },
+    { srcatop_modeproc, SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff },
+    { dstatop_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kSA_Coeff },
+    { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
+
+    { plus_modeproc,    SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff },
+    { multiply_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 },
+    { lighten_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { colordodge_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { colorburn_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { hardlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { softlight_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { difference_modeproc,  CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+    { exclusion_modeproc,   CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
+    return false;
+}
+
+bool SkXfermode::asMode(Mode* mode) {
+    return false;
+}
+
+SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) {
+    // 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) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            dst[i] = this->xferColor(src[i], dst[i]);
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = dst[i];
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = C;
+            }
+        }
+    }
+}
+
+void SkXfermode::xfer16(uint16_t* dst,
+                        const SkPMColor* SK_RESTRICT src, int count,
+                        const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+            dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = SkPixel32ToPixel16_ToU16(C);
+            }
+        }
+    }
+}
+
+void SkXfermode::xfer4444(SkPMColor16* SK_RESTRICT dst,
+                          const SkPMColor* SK_RESTRICT src, int count,
+                          const SkAlpha* SK_RESTRICT aa)
+{
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+            dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = SkPixel32ToPixel4444(C);
+            }
+        }
+    }
+}
+
+void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
+                        const SkPMColor src[], int count,
+                        const SkAlpha* SK_RESTRICT aa)
+{
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
+            dst[i] = SkToU8(SkGetPackedA32(res));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkAlpha dstA = dst[i];
+                unsigned A = SkGetPackedA32(this->xferColor(src[i],
+                                            (SkPMColor)(dstA << SK_A32_SHIFT)));
+                if (0xFF != a) {
+                    A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+                }
+                dst[i] = SkToU8(A);
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkProcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
+                            const SkPMColor* SK_RESTRICT src, int count,
+                            const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                dst[i] = proc(src[i], dst[i]);
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = dst[i];
+                    SkPMColor C = proc(src[i], dstC);
+                    if (a != 0xFF) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = C;
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xfer16(uint16_t* SK_RESTRICT dst,
+                            const SkPMColor* SK_RESTRICT src, int count,
+                            const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                    SkPMColor C = proc(src[i], dstC);
+                    if (0xFF != a) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = SkPixel32ToPixel16_ToU16(C);
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xfer4444(SkPMColor16* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src, int count,
+                              const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                    SkPMColor C = proc(src[i], dstC);
+                    if (0xFF != a) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = SkPixel32ToPixel4444(C);
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
+                            const SkPMColor* SK_RESTRICT src, int count,
+                            const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
+                dst[i] = SkToU8(SkGetPackedA32(res));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkAlpha dstA = dst[i];
+                    SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
+                    unsigned A = SkGetPackedA32(res);
+                    if (0xFF != a) {
+                        A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+                    }
+                    dst[i] = SkToU8(A);
+                }
+            }
+        }
+    }
+}
+
+SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
+        : SkXfermode(buffer) {
+    // Might be a NULL if the Xfermode is recorded using the CrossProcess flag
+    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 {
+        buffer.writeFunctionPtr((void*)fProc);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class SkProcCoeffXfermode : public SkProcXfermode {
+public:
+    SkProcCoeffXfermode(const ProcCoeff& rec, Mode mode)
+            : INHERITED(rec.fProc) {
+        fMode = mode;
+        // these may be valid, or may be CANNOT_USE_COEFF
+        fSrcCoeff = rec.fSC;
+        fDstCoeff = rec.fDC;
+    }
+
+    virtual bool asMode(Mode* mode) {
+        if (mode) {
+            *mode = fMode;
+        }
+        return true;
+    }
+
+    virtual bool asCoeff(Coeff* sc, Coeff* dc) {
+        if (CANNOT_USE_COEFF == fSrcCoeff) {
+            return false;
+        }
+
+        if (sc) {
+            *sc = fSrcCoeff;
+        }
+        if (dc) {
+            *dc = fDstCoeff;
+        }
+        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));
+    }
+
+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;
+        }
+
+        const ProcCoeff& rec = gProcCoeffs[fMode];
+        // these may be valid, or may be CANNOT_USE_COEFF
+        fSrcCoeff = rec.fSC;
+        fDstCoeff = rec.fDC;
+        // now update our function-ptr in the super class
+        this->INHERITED::setProc(rec.fProc);
+    }
+
+private:
+    Mode    fMode;
+    Coeff   fSrcCoeff, fDstCoeff;
+
+
+    typedef SkProcXfermode INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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; }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkClearXfermode, (buffer));
+    }
+
+private:
+    SkClearXfermode(SkFlattenableReadBuffer& buffer)
+        : SkProcCoeffXfermode(buffer) {}
+
+};
+
+void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT, int count,
+                             const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && count >= 0);
+
+    if (NULL == aa) {
+        memset(dst, 0, count << 2);
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0xFF == a) {
+                dst[i] = 0;
+            } else if (a != 0) {
+                dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
+            }
+        }
+    }
+}
+void SkClearXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT, int count,
+                             const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && count >= 0);
+
+    if (NULL == aa) {
+        memset(dst, 0, count);
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0xFF == a) {
+                dst[i] = 0;
+            } else if (0 != a) {
+                dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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; }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkSrcXfermode, (buffer));
+    }
+
+private:
+    SkSrcXfermode(SkFlattenableReadBuffer& buffer)
+        : SkProcCoeffXfermode(buffer) {}
+
+};
+
+void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
+                           const SkPMColor* SK_RESTRICT src, int count,
+                           const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        memcpy(dst, src, count << 2);
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (a == 0xFF) {
+                dst[i] = src[i];
+            } else if (a != 0) {
+                dst[i] = SkFourByteInterp(src[i], dst[i], a);
+            }
+        }
+    }
+}
+
+void SkSrcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
+                           const SkPMColor* SK_RESTRICT src, int count,
+                           const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            dst[i] = SkToU8(SkGetPackedA32(src[i]));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                unsigned srcA = SkGetPackedA32(src[i]);
+                if (a == 0xFF) {
+                    dst[i] = SkToU8(srcA);
+                } else {
+                    dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a));
+                }
+            }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+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; }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkDstInXfermode, (buffer));
+    }
+
+private:
+    SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    typedef SkProcCoeffXfermode INHERITED;
+};
+
+void SkDstInXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT src, int count,
+                             const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src);
+
+    if (count <= 0) {
+        return;
+    }
+    if (NULL != aa) {
+        return this->INHERITED::xfer32(dst, src, count, aa);
+    }
+
+    do {
+        unsigned a = SkGetPackedA32(*src);
+        *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
+        dst++;
+        src++;
+    } while (--count != 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+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; }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkDstOutXfermode, (buffer));
+    }
+
+private:
+    SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+    typedef SkProcCoeffXfermode INHERITED;
+};
+
+void SkDstOutXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src, int count,
+                              const SkAlpha* SK_RESTRICT aa) {
+    SkASSERT(dst && src);
+
+    if (count <= 0) {
+        return;
+    }
+    if (NULL != aa) {
+        return this->INHERITED::xfer32(dst, src, count, aa);
+    }
+
+    do {
+        unsigned a = SkGetPackedA32(*src);
+        *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
+        dst++;
+        src++;
+    } while (--count != 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkXfermode* SkXfermode::Create(Mode mode) {
+    SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
+    SkASSERT((unsigned)mode < kModeCount);
+
+    const ProcCoeff& rec = gProcCoeffs[mode];
+
+    switch (mode) {
+        case kClear_Mode:
+            return SkNEW_ARGS(SkClearXfermode, (rec));
+        case kSrc_Mode:
+            return SkNEW_ARGS(SkSrcXfermode, (rec));
+        case kSrcOver_Mode:
+            return NULL;
+        case kDstIn_Mode:
+            return SkNEW_ARGS(SkDstInXfermode, (rec));
+        case kDstOut_Mode:
+            return SkNEW_ARGS(SkDstOutXfermode, (rec));
+        default:
+            return SkNEW_ARGS(SkProcCoeffXfermode, (rec, mode));
+    }
+}
+
+SkXfermodeProc SkXfermode::GetProc(Mode mode) {
+    SkXfermodeProc  proc = NULL;
+    if ((unsigned)mode < kModeCount) {
+        proc = gProcCoeffs[mode].fProc;
+    }
+    return proc;
+}
+
+bool SkXfermode::ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst) {
+    SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
+
+    if ((unsigned)mode >= (unsigned)kModeCount) {
+        // illegal mode parameter
+        return false;
+    }
+
+    const ProcCoeff& rec = gProcCoeffs[mode];
+
+    if (CANNOT_USE_COEFF == rec.fSC) {
+        return false;
+    }
+
+    SkASSERT(CANNOT_USE_COEFF != rec.fDC);
+    if (src) {
+        *src = rec.fSC;
+    }
+    if (dst) {
+        *dst = rec.fDC;
+    }
+    return true;
+}
+
+bool SkXfermode::AsMode(SkXfermode* xfer, Mode* mode) {
+    if (NULL == xfer) {
+        if (mode) {
+            *mode = kSrcOver_Mode;
+        }
+        return true;
+    }
+    return xfer->asMode(mode);
+}
+
+bool SkXfermode::AsCoeff(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) {
+    // if xfer==null then the mode is srcover
+    Mode m = kSrcOver_Mode;
+    if (xfer && !xfer->asMode(&m)) {
+        return false;
+    }
+    return mode == m;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//////////// 16bit xfermode procs
+
+#ifdef SK_DEBUG
+static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
+static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
+#endif
+
+static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
+    return dst;
+}
+
+static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return dst;
+}
+
+static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return dst;
+}
+
+static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
+    unsigned isa = 255 - SkGetPackedA32(src);
+
+    return SkPackRGB16(
+           SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
+           SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
+           SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
+}
+
+static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return dst;
+}
+
+/*********
+    darken and lighten boil down to this.
+
+    darken  = (1 - Sa) * Dc + min(Sc, Dc)
+    lighten = (1 - Sa) * Dc + max(Sc, Dc)
+
+    if (Sa == 0) these become
+        darken  = Dc + min(0, Dc) = 0
+        lighten = Dc + max(0, Dc) = Dc
+
+    if (Sa == 1) these become
+        darken  = min(Sc, Dc)
+        lighten = max(Sc, Dc)
+*/
+
+static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return 0;
+}
+
+static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
+    unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
+    unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
+    return SkPackRGB16(r, g, b);
+}
+
+static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
+    unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
+    unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
+    return SkPackRGB16(r, g, b);
+}
+
+struct Proc16Rec {
+    SkXfermodeProc16    fProc16_0;
+    SkXfermodeProc16    fProc16_255;
+    SkXfermodeProc16    fProc16_General;
+};
+
+static const Proc16Rec gModeProcs16[] = {
+    { NULL,                 NULL,                   NULL            }, // CLEAR
+    { NULL,                 src_modeproc16_255,     NULL            },
+    { dst_modeproc16,       dst_modeproc16,         dst_modeproc16  },
+    { srcover_modeproc16_0, srcover_modeproc16_255, NULL            },
+    { dstover_modeproc16_0, dstover_modeproc16_255, NULL            },
+    { NULL,                 srcin_modeproc16_255,   NULL            },
+    { NULL,                 dstin_modeproc16_255,   NULL            },
+    { NULL,                 NULL,                   NULL            },// SRC_OUT
+    { dstout_modeproc16_0,  NULL,                   NULL            },
+    { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16  },
+    { NULL,                 dstatop_modeproc16_255, NULL            },
+    { NULL,                 NULL,                   NULL            }, // XOR
+
+    { NULL,                 NULL,                   NULL            }, // plus
+    { NULL,                 NULL,                   NULL            }, // multiply
+    { NULL,                 NULL,                   NULL            }, // screen
+    { NULL,                 NULL,                   NULL            }, // overlay
+    { darken_modeproc16_0,  darken_modeproc16_255,  NULL            }, // darken
+    { lighten_modeproc16_0, lighten_modeproc16_255, NULL            }, // lighten
+    { NULL,                 NULL,                   NULL            }, // colordodge
+    { NULL,                 NULL,                   NULL            }, // colorburn
+    { NULL,                 NULL,                   NULL            }, // hardlight
+    { NULL,                 NULL,                   NULL            }, // softlight
+    { NULL,                 NULL,                   NULL            }, // difference
+    { NULL,                 NULL,                   NULL            }, // exclusion
+};
+
+SkXfermodeProc16 SkXfermode::GetProc16(Mode mode, SkColor srcColor) {
+    SkXfermodeProc16  proc16 = NULL;
+    if ((unsigned)mode < kModeCount) {
+        const Proc16Rec& rec = gModeProcs16[mode];
+        unsigned a = SkColorGetA(srcColor);
+
+        if (0 == a) {
+            proc16 = rec.fProc16_0;
+        } else if (255 == a) {
+            proc16 = rec.fProc16_255;
+        } else {
+            proc16 = rec.fProc16_General;
+        }
+    }
+    return proc16;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkProcCoeffXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkClearXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSrcXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstInXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDstOutXfermode)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/legacy/src/core/asm/s32a_d565_opaque.S b/legacy/src/core/asm/s32a_d565_opaque.S
new file mode 100644
index 0000000..1154e3f
--- /dev/null
+++ b/legacy/src/core/asm/s32a_d565_opaque.S
@@ -0,0 +1,88 @@
+/* s32a_d565_opaque.S
+**
+** Copyright 2009, 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.
+*/
+
+
+        .text
+        .align
+
+        .global s32a_d565_opaque_arm
+
+// void s32a_d565_opaque_arm(uint16_t*, uint32_t*, size_t)
+// r0: dst ptr
+// r1: src prt
+// r2: count
+
+s32a_d565_opaque_arm:
+    stmdb sp!, {r4, r5, r6, r7, lr}
+    subs r4, r2, #0
+    mov r7, r1
+    ble 1f
+4:  ldr r1, [r7], #4 // r1=*src; src++
+    cmp r1, #0
+    mov lr, r1, lsr #24
+    beq 2f // get next src
+
+    cmp lr, #255 // 0xff
+    mov r2, r1, lsl #8
+    mov r6, r2
+    moveq r2, r1, lsr #5
+    andeq r3, r6, #63488 // 0xf800
+    andeq r2, r2, #2016 // 0x7e0
+    orreq r3, r3, r2
+    orreq r3, r3, r6, lsr #27
+    streqh r3, [r0], #2 // *dst = r3; dst++
+    beq 3f // go to count--
+
+    mov r3, r1, lsl #16
+    rsb ip, lr, #255 // 0xff
+    mov r5, r3, lsr #24
+    ldrh r3, [r0]    // r3 = *dst
+    mov lr, r6, lsr #24
+    and r6, r1, #255 // 0xff
+
+    mov r1, r3, lsr #5
+    and r2, r1, #63
+    smulbb r1, r2, ip
+    add r2, r1, #32
+    mov r1, r3, lsr #11
+    add r2, r2, r2, lsr #6
+
+    smulbb r1, r1, ip
+    add r2, r5, r2, lsr #6
+    add r1, r1, #16
+    and r3, r3, #31 // 0x1f
+    add r1, r1, r1, lsr #5
+
+    smulbb r3, r3, ip
+    add r1, r6, r1, lsr #5
+    mov ip, r2, lsr #2
+    add r3, r3, #16 // 0x10
+    mov r1, r1, lsr #3
+
+    add r3, r3, r3, lsr #5
+    mov r2, ip, lsl #5
+    add r3, lr, r3, lsr #5
+    orr ip, r2, r1, lsl #11
+    orr r1, ip, r3, lsr #3
+    strh r1, [r0]
+
+2:  add r0, r0, #2  // dst++
+3:  subs r4, r4, #1 // r4 = --count
+    bne 4b
+
+1:  ldmia sp!, {r4, r5, r6, r7, pc}
+
diff --git a/legacy/src/effects/Sk1DPathEffect.cpp b/legacy/src/effects/Sk1DPathEffect.cpp
new file mode 100644
index 0000000..9ccc453
--- /dev/null
+++ b/legacy/src/effects/Sk1DPathEffect.cpp
@@ -0,0 +1,187 @@
+
+/*
+ * 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 "Sk1DPathEffect.h"
+#include "SkPathMeasure.h"
+
+bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+    SkPathMeasure   meas(src, false);
+    do {
+        SkScalar    length = meas.getLength();
+        SkScalar    distance = this->begin(length);
+        while (distance < length) {
+            SkScalar delta = this->next(dst, distance, meas);
+            if (delta <= 0) {
+                break;
+            }
+            distance += delta;
+        }
+    } while (meas.nextContour());
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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
+    } else {
+        // cleanup their phase parameter, inverting it so that it becomes an
+        // offset along the path (to match the interpretation in PostScript)
+        if (phase < 0) {
+            phase = -phase;
+            if (phase > advance) {
+                phase = SkScalarMod(phase, advance);
+            }
+        } else {
+            if (phase > advance) {
+                phase = SkScalarMod(phase, advance);
+            }
+            phase = advance - phase;
+        }
+        // now catch the edge case where phase == advance (within epsilon)
+        if (phase >= advance) {
+            phase = 0;
+        }
+        SkASSERT(phase >= 0);
+
+        fAdvance = advance;
+        fInitialOffset = phase;
+        
+        if ((unsigned)style >= kStyleCount) {
+            SkDEBUGF(("SkPath1DPathEffect style enum out of range %d\n", style));
+        }
+        fStyle = style;
+    }
+}
+
+bool SkPath1DPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                    SkScalar* width) {
+    if (fAdvance > 0) {
+        *width = -1;
+        return this->INHERITED::filterPath(dst, src, width);
+    }
+    return false;
+}
+
+static void 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);
+        
+        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);
+    }
+}
+
+/*  TODO
+
+Need differentially more subdivisions when the follow-path is curvy. Not sure how to
+determine that, but we need it. I guess a cheap answer is let the caller tell us,
+but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
+*/
+static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
+                      SkScalar dist) {
+    SkPath::Iter    iter(src, false);
+    SkPoint         srcP[4], dstP[3];
+    SkPath::Verb    verb;
+
+    while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                morphpoints(dstP, srcP, 1, meas, dist);
+                dst->moveTo(dstP[0]);
+                break;
+            case SkPath::kLine_Verb:
+                srcP[2] = srcP[1];
+                srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX),
+                            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]);
+                break;
+            case SkPath::kCubic_Verb:
+                morphpoints(dstP, &srcP[1], 3, meas, dist);
+                dst->cubicTo(dstP[0], dstP[1], dstP[2]);
+                break;
+            case SkPath::kClose_Verb:
+                dst->close();
+                break;
+            default:
+                SkDEBUGFAIL("unknown verb");
+                break;
+        }
+    }
+}
+
+SkPath1DPathEffect::SkPath1DPathEffect(SkFlattenableReadBuffer& buffer) {
+    fAdvance = buffer.readScalar();
+    if (fAdvance > 0) {
+        fPath.unflatten(buffer);
+        fInitialOffset = buffer.readScalar();
+        fStyle = (Style) buffer.readU8();
+    }
+}
+
+SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) {
+    return fInitialOffset;
+}
+
+void SkPath1DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+    buffer.writeScalar(fAdvance);
+    if (fAdvance > 0) {
+        fPath.flatten(buffer);
+        buffer.writeScalar(fInitialOffset);
+        buffer.write8(fStyle);
+    }
+}
+
+SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance,
+                                  SkPathMeasure& meas) {
+    switch (fStyle) {
+        case kTranslate_Style: {
+            SkPoint pos;
+            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);
+        } break;
+        case kMorph_Style:
+            morphpath(dst, fPath, meas, distance);
+            break;
+        default:
+            SkDEBUGFAIL("unknown Style enum");
+            break;
+    }
+    return fAdvance;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkPath1DPathEffect)
+
diff --git a/legacy/src/effects/Sk2DPathEffect.cpp b/legacy/src/effects/Sk2DPathEffect.cpp
new file mode 100644
index 0000000..3729610
--- /dev/null
+++ b/legacy/src/effects/Sk2DPathEffect.cpp
@@ -0,0 +1,121 @@
+
+/*
+ * 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 "Sk2DPathEffect.h"
+#include "SkBlitter.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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+Sk2DPathEffect::Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat) {
+    mat.invert(&fInverse);
+}
+
+bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+    Sk2DPathEffectBlitter   blitter(this, dst);
+    SkPath                  tmp;
+    SkIRect                 ir;
+
+    src.transform(fInverse, &tmp);
+    tmp.getBounds().round(&ir);
+    if (!ir.isEmpty()) {
+        this->begin(ir, dst);
+        SkScan::FillPath(tmp, ir, &blitter);
+        this->end(dst);
+    }
+    return true;
+}
+
+void Sk2DPathEffect::nextSpan(int x, int y, int count, SkPath* path) {
+    const SkMatrix& mat = this->getMatrix();
+    SkPoint src, dst;
+
+    src.set(SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf);
+    do {
+        mat.mapPoints(&dst, &src, 1);
+        this->next(dst, x++, y, path);
+        src.fX += SK_Scalar1;
+    } 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::flatten(SkFlattenableWriteBuffer& buffer) {
+    char storage[SkMatrix::kMaxFlattenSize];
+    uint32_t size = fMatrix.flatten(storage);
+    buffer.write32(size);
+    buffer.write(storage, size);
+}
+
+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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkPath2DPathEffect::SkPath2DPathEffect(const SkMatrix& m, const SkPath& p)
+    : INHERITED(m), fPath(p) {
+}
+
+SkPath2DPathEffect::SkPath2DPathEffect(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fPath.unflatten(buffer);
+}
+
+SkFlattenable* SkPath2DPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkPath2DPathEffect, (buffer));
+}
+
+void SkPath2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    fPath.flatten(buffer);
+}
+
+SkFlattenable::Factory SkPath2DPathEffect::getFactory() {
+    return CreateProc;
+}
+
+void SkPath2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) {
+    dst->addPath(fPath, loc.fX, loc.fY);
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkPath2DPathEffect)
+
diff --git a/legacy/src/effects/SkArithmeticMode.cpp b/legacy/src/effects/SkArithmeticMode.cpp
new file mode 100644
index 0000000..8190b39
--- /dev/null
+++ b/legacy/src/effects/SkArithmeticMode.cpp
@@ -0,0 +1,177 @@
+#include "SkArithmeticMode.h"
+#include "SkColorPriv.h"
+#include "SkUnPreMultiply.h"
+
+class SkArithmeticMode_scalar : public SkXfermode {
+public:
+    SkArithmeticMode_scalar(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4) {
+        fK[0] = k1;
+        fK[1] = k2;
+        fK[2] = k3;
+        fK[3] = k4;
+    }
+
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) SK_OVERRIDE;
+    virtual Factory getFactory() SK_OVERRIDE;
+
+    static SkFlattenable* Create(SkFlattenableReadBuffer& buffer) {
+        return NULL;
+    }
+
+private:
+    SkScalar fK[4];
+};
+
+SkFlattenable::Factory SkArithmeticMode_scalar::getFactory() {
+    return Create;
+}
+
+static int pinToByte(int value) {
+    if (value < 0) {
+        value = 0;
+    } else if (value > 255) {
+        value = 255;
+    }
+    return value;
+}
+
+static int arith(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4,
+                 int src, int dst) {
+    SkScalar result = SkScalarMul(k1, src * dst) +
+                      SkScalarMul(k2, src) +
+                      SkScalarMul(k3, dst) +
+                      k4;
+    int res = SkScalarRoundToInt(result);
+    return pinToByte(res);
+}
+
+static int blend(int src, int dst, int scale) {
+    return dst + ((src - dst) * scale >> 8);
+}
+
+static bool needsUnpremul(int alpha) {
+    return 0 != alpha && 0xFF != alpha;
+}
+
+void SkArithmeticMode_scalar::xfer32(SkPMColor dst[], const SkPMColor src[],
+                                     int count, const SkAlpha aaCoverage[]) {
+    SkScalar k1 = fK[0] / 255;
+    SkScalar k2 = fK[1];
+    SkScalar k3 = fK[2];
+    SkScalar k4 = fK[3] * 255;
+
+    for (int i = 0; i < count; ++i) {
+        if ((NULL == aaCoverage) || aaCoverage[i]) {
+            SkPMColor sc = src[i];
+            SkPMColor dc = dst[i];
+            int sa = SkGetPackedA32(sc);
+            int da = SkGetPackedA32(dc);
+
+            int srcNeedsUnpremul = needsUnpremul(sa);
+            int dstNeedsUnpremul = needsUnpremul(sa);
+
+            int a, r, g, b;
+
+            if (!srcNeedsUnpremul && !srcNeedsUnpremul) {
+                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));
+                b = arith(k1, k2, k3, k4, SkGetPackedB32(sc), SkGetPackedB32(dc));
+            } else {
+                int sr = SkGetPackedR32(sc);
+                int sg = SkGetPackedG32(sc);
+                int sb = SkGetPackedB32(sc);
+                if (srcNeedsUnpremul) {
+                    SkUnPreMultiply::Scale scale = SkUnPreMultiply::GetScale(sa);
+                    sr = SkUnPreMultiply::ApplyScale(scale, sr);
+                    sg = SkUnPreMultiply::ApplyScale(scale, sg);
+                    sb = SkUnPreMultiply::ApplyScale(scale, sb);
+                }
+
+                int dr = SkGetPackedR32(dc);
+                int dg = SkGetPackedG32(dc);
+                int db = SkGetPackedB32(dc);
+                if (dstNeedsUnpremul) {
+                    SkUnPreMultiply::Scale scale = SkUnPreMultiply::GetScale(da);
+                    dr = SkUnPreMultiply::ApplyScale(scale, dr);
+                    dg = SkUnPreMultiply::ApplyScale(scale, dg);
+                    db = SkUnPreMultiply::ApplyScale(scale, db);
+                }
+
+                a = arith(k1, k2, k3, k4, sa, sa);
+                r = arith(k1, k2, k3, k4, sr, dr);
+                g = arith(k1, k2, k3, k4, sg, dg);
+                b = arith(k1, k2, k3, k4, sb, db);
+            }
+
+            // apply antialias coverage if necessary
+            if (aaCoverage && 0xFF != aaCoverage[i]) {
+                int scale = aaCoverage[i] + (aaCoverage[i] >> 7);
+                a = blend(a, SkGetPackedA32(sc), scale);
+                r = blend(r, SkGetPackedR32(sc), scale);
+                g = blend(g, SkGetPackedG32(sc), scale);
+                b = blend(b, SkGetPackedB32(sc), scale);
+            }
+
+            // turn the result back into premul
+            if (0xFF != a) {
+                int scale = a + (a >> 7);
+                r = SkAlphaMul(r, scale);
+                g = SkAlphaMul(g, scale);
+                b = SkAlphaMul(b, scale);
+            }
+            dst[i] = SkPackARGB32(a, r, g, b);
+        }
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool fitsInBits(SkScalar x, int bits) {
+#ifdef SK_SCALAR_IS_FIXED
+    x = SkAbs32(x);
+    x += 1 << 7;
+    x >>= 8;
+    return x < (1 << (bits - 1));
+#else
+    return SkScalarAbs(x) < (1 << (bits - 1));
+#endif
+}
+
+static int32_t toDot8(SkScalar x) {
+#ifdef SK_SCALAR_IS_FIXED
+    x += 1 << 7;
+    x >>= 8;
+    return x;
+#else
+    return (int32_t)(x * 256);
+#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)) {
+
+        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));
+        }
+        if (0 == i2) {
+            return SkNEW_ARGS(SkArithmeticMode_dst, (i3, i4));
+        }
+        if (0 == i3) {
+            return SkNEW_ARGS(SkArithmeticMode_src, (i2, i4));
+        }
+        return SkNEW_ARGS(SkArithmeticMode_linear, (i2, i3, i4));
+#endif
+    }
+    return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4));
+}
+
diff --git a/legacy/src/effects/SkAvoidXfermode.cpp b/legacy/src/effects/SkAvoidXfermode.cpp
new file mode 100644
index 0000000..9f4e396
--- /dev/null
+++ b/legacy/src/effects/SkAvoidXfermode.cpp
@@ -0,0 +1,243 @@
+
+/*
+ * 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 "SkAvoidXfermode.h"
+#include "SkColorPriv.h"
+
+SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode)
+{
+    if (tolerance > 255) {
+        tolerance = 255;
+    }
+
+    fOpColor = opColor;
+    fDistMul = (256 << 14) / (tolerance + 1);
+    fMode = mode;
+}
+
+SkAvoidXfermode::SkAvoidXfermode(SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer)
+{
+    fOpColor = buffer.readU32();
+    fDistMul = buffer.readU32();
+    fMode = (Mode)buffer.readU8();
+}
+
+void SkAvoidXfermode::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    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;
+}
+
+// returns 0..31
+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);
+
+    unsigned dr = SkAbs32(SkGetPackedR16(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS);
+    unsigned db = SkAbs32(SkGetPackedB16(c) - b);
+
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+// returns 0..15
+static unsigned color_dist4444(uint16_t c, unsigned r, unsigned g, unsigned b)
+{
+    SkASSERT(r <= 0xF);
+    SkASSERT(g <= 0xF);
+    SkASSERT(b <= 0xF);
+
+    unsigned dr = SkAbs32(SkGetPackedR4444(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG4444(c) - g);
+    unsigned db = SkAbs32(SkGetPackedB4444(c) - b);
+
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+// returns 0..255
+static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b)
+{
+    SkASSERT(r <= 0xFF);
+    SkASSERT(g <= 0xFF);
+    SkASSERT(b <= 0xFF);
+
+    unsigned dr = SkAbs32(SkGetPackedR32(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG32(c) - g);
+    unsigned db = SkAbs32(SkGetPackedB32(c) - b);
+
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+static int scale_dist_14(int dist, uint32_t mul, uint32_t sub)
+{
+    int tmp = dist * mul - sub;
+    int result = (tmp + (1 << 13)) >> 14;
+
+    return result;
+}
+
+static inline unsigned Accurate255To256(unsigned x) {
+    return x + (x >> 7);
+}
+
+void SkAvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[])
+{
+    unsigned    opR = SkColorGetR(fOpColor);
+    unsigned    opG = SkColorGetG(fOpColor);
+    unsigned    opB = SkColorGetB(fOpColor);
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << 8;
+
+    int MAX, mask;
+
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 255;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+
+    for (int i = 0; i < count; i++) {
+        int d = color_dist32(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 255);
+        d = Accurate255To256(d);
+
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 256);
+
+        if (d > 0) {
+            if (NULL != aa) {
+                d = SkAlphaMul(d, Accurate255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkFourByteInterp(src[i], dst[i], d);
+        }
+    }
+}
+
+static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale)
+{
+    SkASSERT(scale <= 32);
+    scale <<= 3;
+
+    return SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(src), SkGetPackedR16(dst), scale),
+                        SkAlphaBlend(SkPacked32ToG16(src), SkGetPackedG16(dst), scale),
+                        SkAlphaBlend(SkPacked32ToB16(src), SkGetPackedB16(dst), scale));
+}
+
+void SkAvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[])
+{
+    unsigned    opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS);
+    unsigned    opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS);
+    unsigned    opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS);
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << SK_R16_BITS;
+
+    int MAX, mask;
+
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 31;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+
+    for (int i = 0; i < count; i++) {
+        int d = color_dist16(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 31);
+        // convert from 0..31 to 0..32
+        d += d >> 4;
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 32);
+
+        if (d > 0) {
+            if (NULL != aa) {
+                d = SkAlphaMul(d, Accurate255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkBlend3216(src[i], dst[i], d);
+        }
+    }
+}
+
+void SkAvoidXfermode::xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                               const SkAlpha aa[])
+{
+    unsigned    opR = SkColorGetR(fOpColor) >> 4;
+    unsigned    opG = SkColorGetG(fOpColor) >> 4;
+    unsigned    opB = SkColorGetB(fOpColor) >> 4;
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << 4;
+
+    int MAX, mask;
+
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 15;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+
+    for (int i = 0; i < count; i++) {
+        int d = color_dist4444(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 15);
+        // convert from 0..15 to 0..16
+        d += d >> 3;
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 16);
+
+        if (d > 0) {
+            if (NULL != aa) {
+                d = SkAlphaMul(d, Accurate255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkBlend4444(SkPixel32ToPixel4444(src[i]), dst[i], d);
+        }
+    }
+}
+
+void SkAvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[])
+{
+    // override in subclass
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkAvoidXfermode)
diff --git a/src/effects/SkBitmapCache.cpp b/legacy/src/effects/SkBitmapCache.cpp
similarity index 100%
rename from src/effects/SkBitmapCache.cpp
rename to legacy/src/effects/SkBitmapCache.cpp
diff --git a/src/effects/SkBitmapCache.h b/legacy/src/effects/SkBitmapCache.h
similarity index 100%
rename from src/effects/SkBitmapCache.h
rename to legacy/src/effects/SkBitmapCache.h
diff --git a/legacy/src/effects/SkBlurDrawLooper.cpp b/legacy/src/effects/SkBlurDrawLooper.cpp
new file mode 100644
index 0000000..a44f081
--- /dev/null
+++ b/legacy/src/effects/SkBlurDrawLooper.cpp
@@ -0,0 +1,120 @@
+
+/*
+ * 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 "SkBlurDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkMaskFilter.h"
+#include "SkColorFilter.h"
+
+SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy,
+                                   SkColor color, uint32_t flags)
+    : fDx(dx), fDy(dy), fBlurColor(color), fBlurFlags(flags) {
+
+    SkASSERT(flags <= kAll_BlurFlag);
+    if (radius > 0) {
+        uint32_t blurFlags = flags & kIgnoreTransform_BlurFlag ?
+            SkBlurMaskFilter::kIgnoreTransform_BlurFlag :
+            SkBlurMaskFilter::kNone_BlurFlag;
+
+        blurFlags |= flags & kHighQuality_BlurFlag ?
+            SkBlurMaskFilter::kHighQuality_BlurFlag : 
+            SkBlurMaskFilter::kNone_BlurFlag;
+
+        fBlur = SkBlurMaskFilter::Create(radius,
+                                         SkBlurMaskFilter::kNormal_BlurStyle,  
+                                         blurFlags);
+    } else {
+        fBlur = NULL;
+    }
+
+    if (flags & kOverrideColor_BlurFlag) {
+        // Set alpha to 1 for the override since transparency will already
+        // be baked into the blurred mask.
+        SkColor opaqueColor = SkColorSetA(color, 255);
+        //The SrcIn xfer mode will multiply 'color' by the incoming alpha
+        fColorFilter = SkColorFilter::CreateModeFilter(opaqueColor,
+                                                       SkXfermode::kSrcIn_Mode);
+    } else {
+        fColorFilter = NULL;
+    }
+}
+
+SkBlurDrawLooper::SkBlurDrawLooper(SkFlattenableReadBuffer& buffer)
+: INHERITED(buffer) {
+
+    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;
+}
+
+SkBlurDrawLooper::~SkBlurDrawLooper() {
+    SkSafeUnref(fBlur);
+    SkSafeUnref(fColorFilter);
+}
+
+void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) {
+    buffer.writeScalar(fDx);
+    buffer.writeScalar(fDy);
+    buffer.write32(fBlurColor);
+    buffer.writeFlattenable(fBlur);
+    buffer.writeFlattenable(fColorFilter);
+    buffer.write32(fBlurFlags);
+}
+
+void SkBlurDrawLooper::init(SkCanvas* canvas) {
+    fState = kBeforeEdge;
+}
+
+bool SkBlurDrawLooper::next(SkCanvas* canvas, SkPaint* paint) {
+    switch (fState) {
+        case kBeforeEdge:
+            // we do nothing if a maskfilter is already installed
+            if (paint->getMaskFilter()) {
+                fState = kDone;
+                return false;
+            }
+#ifdef SK_BUILD_FOR_ANDROID
+            SkColor blurColor;
+            blurColor = fBlurColor;
+            if (SkColorGetA(blurColor) == 255) {
+                blurColor = SkColorSetA(blurColor, paint->getAlpha());
+            }
+            paint->setColor(blurColor);
+#else
+            paint->setColor(fBlurColor);
+#endif
+            paint->setMaskFilter(fBlur);
+            paint->setColorFilter(fColorFilter);
+            canvas->save(SkCanvas::kMatrix_SaveFlag);
+            if (fBlurFlags & kIgnoreTransform_BlurFlag) {
+                SkMatrix transform(canvas->getTotalMatrix());
+                transform.postTranslate(fDx, fDy);
+                canvas->setMatrix(transform);
+            } else {
+                canvas->translate(fDx, fDy);
+            }
+            fState = kAfterEdge;
+            return true;
+        case kAfterEdge:
+            canvas->restore();
+            fState = kDone;
+            return true;
+        default:
+            SkASSERT(kDone == fState);
+            return false;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkBlurDrawLooper)
+
diff --git a/legacy/src/effects/SkBlurImageFilter.cpp b/legacy/src/effects/SkBlurImageFilter.cpp
new file mode 100644
index 0000000..1446e92
--- /dev/null
+++ b/legacy/src/effects/SkBlurImageFilter.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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 "SkBlurImageFilter.h"
+#include "SkColorPriv.h"
+
+SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    fSigma.fWidth = buffer.readScalar();
+    fSigma.fHeight = buffer.readScalar();
+}
+
+SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY)
+    : 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) {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fSigma.fWidth);
+    buffer.writeScalar(fSigma.fHeight);
+}
+
+static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
+                     int leftOffset, int rightOffset)
+{
+    int width = src.width(), height = src.height();
+    int rightBorder = SkMin32(rightOffset + 1, width);
+    for (int y = 0; y < height; ++y) {
+        int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
+        SkPMColor* p = src.getAddr32(0, y);
+        for (int i = 0; i < rightBorder; ++i) {
+            sumA += SkGetPackedA32(*p);
+            sumR += SkGetPackedR32(*p);
+            sumG += SkGetPackedG32(*p);
+            sumB += SkGetPackedB32(*p);
+            p++;
+        }
+
+        const SkColor* sptr = src.getAddr32(0, y);
+        SkColor* dptr = dst->getAddr32(0, y);
+        for (int x = 0; x < width; ++x) {
+            *dptr = SkPackARGB32(sumA / kernelSize,
+                                 sumR / kernelSize,
+                                 sumG / kernelSize,
+                                 sumB / kernelSize);
+            if (x >= leftOffset) {
+                SkColor l = *(sptr - leftOffset);
+                sumA -= SkGetPackedA32(l);
+                sumR -= SkGetPackedR32(l);
+                sumG -= SkGetPackedG32(l);
+                sumB -= SkGetPackedB32(l);
+            }
+            if (x + rightOffset + 1 < width) {
+                SkColor r = *(sptr + rightOffset + 1);
+                sumA += SkGetPackedA32(r);
+                sumR += SkGetPackedR32(r);
+                sumG += SkGetPackedG32(r);
+                sumB += SkGetPackedB32(r);
+            }
+            sptr++;
+            dptr++;
+        }
+    }
+}
+
+static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize,
+                     int topOffset, int bottomOffset)
+{
+    int width = src.width(), height = src.height();
+    int bottomBorder = SkMin32(bottomOffset + 1, height);
+    int srcStride = src.rowBytesAsPixels();
+    int dstStride = dst->rowBytesAsPixels();
+    for (int x = 0; x < width; ++x) {
+        int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
+        SkColor* p = src.getAddr32(x, 0);
+        for (int i = 0; i < bottomBorder; ++i) {
+            sumA += SkGetPackedA32(*p);
+            sumR += SkGetPackedR32(*p);
+            sumG += SkGetPackedG32(*p);
+            sumB += SkGetPackedB32(*p);
+            p += srcStride;
+        }
+
+        const SkColor* sptr = src.getAddr32(x, 0);
+        SkColor* dptr = dst->getAddr32(x, 0);
+        for (int y = 0; y < height; ++y) {
+            *dptr = SkPackARGB32(sumA / kernelSize,
+                                 sumR / kernelSize,
+                                 sumG / kernelSize,
+                                 sumB / kernelSize);
+            if (y >= topOffset) {
+                SkColor l = *(sptr - topOffset * srcStride);
+                sumA -= SkGetPackedA32(l);
+                sumR -= SkGetPackedR32(l);
+                sumG -= SkGetPackedG32(l);
+                sumB -= SkGetPackedB32(l);
+            }
+            if (y + bottomOffset + 1 < height) {
+                SkColor r = *(sptr + (bottomOffset + 1) * srcStride);
+                sumA += SkGetPackedA32(r);
+                sumR += SkGetPackedR32(r);
+                sumG += SkGetPackedG32(r);
+                sumB += SkGetPackedB32(r);
+            }
+            sptr += srcStride;
+            dptr += dstStride;
+        }
+    }
+}
+
+static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, int *highOffset)
+{
+    float pi = SkScalarToFloat(SK_ScalarPI);
+    int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
+    *kernelSize = d;
+    if (d % 2 == 1) {
+        *lowOffset = *highOffset = (d - 1) / 2;
+        *kernelSize3 = d;
+    } else {
+        *highOffset = d / 2;
+        *lowOffset = *highOffset - 1;
+        *kernelSize3 = d + 1;
+    }
+}
+
+bool SkBlurImageFilter::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;
+    }
+
+    dst->setConfig(src.config(), src.width(), src.height());
+    dst->allocPixels();
+    int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
+    int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
+    getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
+    getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
+
+    if (kernelSizeX < 0 || kernelSizeY < 0) {
+        return false;
+    }
+
+    if (kernelSizeX == 0 && kernelSizeY == 0) {
+        src.copyTo(dst, dst->config());
+        return true;
+    }
+
+    SkBitmap temp;
+    temp.setConfig(dst->config(), dst->width(), dst->height());
+    if (!temp.allocPixels()) {
+        return false;
+    }
+
+    if (kernelSizeX > 0 && kernelSizeY > 0) {
+        boxBlurX(src,  &temp, kernelSizeX,  lowOffsetX, highOffsetX);
+        boxBlurY(temp, dst,   kernelSizeY,  lowOffsetY, highOffsetY);
+        boxBlurX(*dst, &temp, kernelSizeX,  highOffsetX,  lowOffsetX);
+        boxBlurY(temp, dst,   kernelSizeY,  highOffsetY,  lowOffsetY);
+        boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX);
+        boxBlurY(temp, dst,   kernelSizeY3, highOffsetY, highOffsetY);
+    } else if (kernelSizeX > 0) {
+        boxBlurX(src,  dst,   kernelSizeX,  lowOffsetX, highOffsetX);
+        boxBlurX(*dst, &temp, kernelSizeX,  highOffsetX,  lowOffsetX);
+        boxBlurX(temp, dst,   kernelSizeX3, highOffsetX, highOffsetX);
+    } else if (kernelSizeY > 0) {
+        boxBlurY(src,  dst,   kernelSizeY,  lowOffsetY, highOffsetY);
+        boxBlurY(*dst, &temp, kernelSizeY,  highOffsetY, lowOffsetY);
+        boxBlurY(temp, dst,   kernelSizeY3, highOffsetY, highOffsetY);
+    }
+    return true;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkBlurImageFilter)
diff --git a/legacy/src/effects/SkBlurMask.cpp b/legacy/src/effects/SkBlurMask.cpp
new file mode 100644
index 0000000..95a1e6b
--- /dev/null
+++ b/legacy/src/effects/SkBlurMask.cpp
@@ -0,0 +1,672 @@
+
+/*
+ * 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 "SkBlurMask.h"
+#include "SkMath.h"
+#include "SkTemplates.h"
+#include "SkEndian.h"
+
+// 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
+// us no appreciable speedup on Windows or Mac, and 2% slowdown on Linux.
+#if defined(SK_BUILD_FOR_WIN32)
+#define UNROLL_KERNEL_LOOP 1
+#endif
+
+/** The sum buffer is an array of u32 to hold the accumulated sum of all of the
+    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,
+                             const uint8_t src[], int srcRB) {
+    int sumW = srcW + 1;
+
+    SkASSERT(srcRB >= srcW);
+    // mod srcRB so we can apply it after each row
+    srcRB -= srcW;
+
+    int x, y;
+
+    // zero out the top row and column
+    memset(sum, 0, sumW * sizeof(sum[0]));
+    sum += sumW;
+
+    // special case first row
+    uint32_t X = 0;
+    *sum++ = 0; // initialze the first column to 0
+    for (x = srcW - 1; x >= 0; --x) {
+        X = *src++ + X;
+        *sum++ = X;
+    }
+    src += srcRB;
+
+    // now do the rest of the rows
+    for (y = srcH - 1; y > 0; --y) {
+        uint32_t L = 0;
+        uint32_t C = 0;
+        *sum++ = 0; // initialze the first column to 0
+
+        for (x = srcW - 1; !SkIsAlign4((intptr_t) src) && x >= 0; x--) {
+            uint32_t T = sum[-sumW];
+            X = *src++ + L + T - C;
+            *sum++ = X;
+            L = X;
+            C = T;
+        }
+
+        for (; x >= 4; x-=4) {
+            uint32_t T = sum[-sumW];
+            X = *src++ + L + T - C;
+            *sum++ = X;
+            L = X;
+            C = T;
+            T = sum[-sumW];
+            X = *src++ + L + T - C;
+            *sum++ = X;
+            L = X;
+            C = T;
+            T = sum[-sumW];
+            X = *src++ + L + T - C;
+            *sum++ = X;
+            L = X;
+            C = T;
+            T = sum[-sumW];
+            X = *src++ + L + T - C;
+            *sum++ = X;
+            L = X;
+            C = T;
+        }
+
+        for (; x >= 0; --x) {
+            uint32_t T = sum[-sumW];
+            X = *src++ + L + T - C;
+            *sum++ = X;
+            L = X;
+            C = T;
+        }
+        src += srcRB;
+    }
+}
+
+/**
+ * This is the path for apply_kernel() to be taken when the kernel
+ * is wider than the source image.
+ */
+static void kernel_clamped(uint8_t dst[], int rx, int ry, const uint32_t sum[],
+                           int sw, int sh) {
+    SkASSERT(2*rx > sw);
+
+    uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));
+
+    int sumStride = sw + 1;
+
+    int dw = sw + 2*rx;
+    int dh = sh + 2*ry;
+
+    int prev_y = -2*ry;
+    int next_y = 1;
+
+    for (int y = 0; y < dh; y++) {
+        int py = SkClampPos(prev_y) * sumStride;
+        int ny = SkFastMin32(next_y, sh) * sumStride;
+
+        int prev_x = -2*rx;
+        int next_x = 1;
+
+        for (int x = 0; x < dw; x++) {
+            int px = SkClampPos(prev_x);
+            int nx = SkFastMin32(next_x, sw);
+
+            uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
+            *dst++ = SkToU8(tmp * scale >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+
+        prev_y += 1;
+        next_y += 1;
+    }
+}
+/**
+ *  sw and sh are the width and height of the src. Since the sum buffer
+ *  matches that, but has an extra row and col at the beginning (with zeros),
+ *  we can just use sw and sh as our "max" values for pinning coordinates
+ *  when sampling into sum[][]
+ *
+ *  The inner loop is conceptually simple; we break it into several sections
+ *  to improve performance. Here's the original version:
+        for (int x = 0; x < dw; x++) {
+            int px = SkClampPos(prev_x);
+            int nx = SkFastMin32(next_x, sw);
+
+            uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
+            *dst++ = SkToU8(tmp * scale >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+ *  The sections are:
+ *     left-hand section, where prev_x is clamped to 0
+ *     center section, where neither prev_x nor next_x is clamped
+ *     right-hand section, where next_x is clamped to sw
+ *  On some operating systems, the center section is unrolled for additional
+ *  speedup.
+*/
+static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
+                         int sw, int sh) {
+    if (2*rx > sw) {
+        kernel_clamped(dst, rx, ry, sum, sw, sh);
+        return;
+    }
+
+    uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));
+
+    int sumStride = sw + 1;
+
+    int dw = sw + 2*rx;
+    int dh = sh + 2*ry;
+
+    int prev_y = -2*ry;
+    int next_y = 1;
+
+    SkASSERT(2*rx <= dw - 2*rx);
+
+    for (int y = 0; y < dh; y++) {
+        int py = SkClampPos(prev_y) * sumStride;
+        int ny = SkFastMin32(next_y, sh) * sumStride;
+
+        int prev_x = -2*rx;
+        int next_x = 1;
+        int x = 0;
+
+        for (; x < 2*rx; x++) {
+            SkASSERT(prev_x <= 0);
+            SkASSERT(next_x <= sw);
+
+            int px = 0;
+            int nx = next_x;
+
+            uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
+            *dst++ = SkToU8(tmp * scale >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+
+        int i0 = prev_x + py;
+        int i1 = next_x + ny;
+        int i2 = next_x + py;
+        int i3 = prev_x + ny;
+
+#if UNROLL_KERNEL_LOOP
+        for (; x < dw - 2*rx - 4; x += 4) {
+            SkASSERT(prev_x >= 0);
+            SkASSERT(next_x <= sw);
+
+            uint32_t tmp = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            *dst++ = SkToU8(tmp * scale >> 24);
+            tmp = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            *dst++ = SkToU8(tmp * scale >> 24);
+            tmp = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            *dst++ = SkToU8(tmp * scale >> 24);
+            tmp = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            *dst++ = SkToU8(tmp * scale >> 24);
+
+            prev_x += 4;
+            next_x += 4;
+        }
+#endif
+
+        for (; x < dw - 2*rx; x++) {
+            SkASSERT(prev_x >= 0);
+            SkASSERT(next_x <= sw);
+
+            uint32_t tmp = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            *dst++ = SkToU8(tmp * scale >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+
+        for (; x < dw; x++) {
+            SkASSERT(prev_x >= 0);
+            SkASSERT(next_x > sw);
+
+            int px = prev_x;
+            int nx = sw;
+
+            uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
+            *dst++ = SkToU8(tmp * scale >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+
+        prev_y += 1;
+        next_y += 1;
+    }
+}
+
+/**
+ * This is the path for apply_kernel_interp() to be taken when the kernel
+ * is wider than the source image.
+ */
+static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
+                const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
+    SkASSERT(2*rx > sw);
+
+    int inner_weight = 255 - outer_weight;
+
+    // round these guys up if they're bigger than 127
+    outer_weight += outer_weight >> 7;
+    inner_weight += inner_weight >> 7;
+
+    uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
+    uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
+
+    int sumStride = sw + 1;
+
+    int dw = sw + 2*rx;
+    int dh = sh + 2*ry;
+
+    int prev_y = -2*ry;
+    int next_y = 1;
+
+    for (int y = 0; y < dh; y++) {
+        int py = SkClampPos(prev_y) * sumStride;
+        int ny = SkFastMin32(next_y, sh) * sumStride;
+
+        int ipy = SkClampPos(prev_y + 1) * sumStride;
+        int iny = SkClampMax(next_y - 1, sh) * sumStride;
+
+        int prev_x = -2*rx;
+        int next_x = 1;
+
+        for (int x = 0; x < dw; x++) {
+            int px = SkClampPos(prev_x);
+            int nx = SkFastMin32(next_x, sw);
+
+            int ipx = SkClampPos(prev_x + 1);
+            int inx = SkClampMax(next_x - 1, sw);
+
+            uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+                               - sum[nx+py] - sum[px+ny];
+            uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+                               - sum[inx+ipy] - sum[ipx+iny];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+        prev_y += 1;
+        next_y += 1;
+    }
+}
+
+/**
+ *  sw and sh are the width and height of the src. Since the sum buffer
+ *  matches that, but has an extra row and col at the beginning (with zeros),
+ *  we can just use sw and sh as our "max" values for pinning coordinates
+ *  when sampling into sum[][]
+ *
+ *  The inner loop is conceptually simple; we break it into several variants
+ *  to improve performance. Here's the original version:
+        for (int x = 0; x < dw; x++) {
+            int px = SkClampPos(prev_x);
+            int nx = SkFastMin32(next_x, sw);
+
+            int ipx = SkClampPos(prev_x + 1);
+            int inx = SkClampMax(next_x - 1, sw);
+
+            uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+                               - sum[nx+py] - sum[px+ny];
+            uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+                               - sum[inx+ipy] - sum[ipx+iny];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+ *  The sections are:
+ *     left-hand section, where prev_x is clamped to 0
+ *     center section, where neither prev_x nor next_x is clamped
+ *     right-hand section, where next_x is clamped to sw
+ *  On some operating systems, the center section is unrolled for additional
+ *  speedup.
+*/
+static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
+                const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
+    SkASSERT(rx > 0 && ry > 0);
+    SkASSERT(outer_weight <= 255);
+
+    if (2*rx > sw) {
+        kernel_interp_clamped(dst, rx, ry, sum, sw, sh, outer_weight);
+        return;
+    }
+
+    int inner_weight = 255 - outer_weight;
+
+    // round these guys up if they're bigger than 127
+    outer_weight += outer_weight >> 7;
+    inner_weight += inner_weight >> 7;
+
+    uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
+    uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
+
+    int sumStride = sw + 1;
+
+    int dw = sw + 2*rx;
+    int dh = sh + 2*ry;
+
+    int prev_y = -2*ry;
+    int next_y = 1;
+
+    SkASSERT(2*rx <= dw - 2*rx);
+
+    for (int y = 0; y < dh; y++) {
+        int py = SkClampPos(prev_y) * sumStride;
+        int ny = SkFastMin32(next_y, sh) * sumStride;
+
+        int ipy = SkClampPos(prev_y + 1) * sumStride;
+        int iny = SkClampMax(next_y - 1, sh) * sumStride;
+
+        int prev_x = -2*rx;
+        int next_x = 1;
+        int x = 0;
+
+        for (; x < 2*rx; x++) {
+            SkASSERT(prev_x < 0);
+            SkASSERT(next_x <= sw);
+
+            int px = 0;
+            int nx = next_x;
+
+            int ipx = 0;
+            int inx = next_x - 1;
+
+            uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+                               - sum[nx+py] - sum[px+ny];
+            uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+                               - sum[inx+ipy] - sum[ipx+iny];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+
+        int i0 = prev_x + py;
+        int i1 = next_x + ny;
+        int i2 = next_x + py;
+        int i3 = prev_x + ny;
+        int i4 = prev_x + 1 + ipy;
+        int i5 = next_x - 1 + iny;
+        int i6 = next_x - 1 + ipy;
+        int i7 = prev_x + 1 + iny;
+
+#if UNROLL_KERNEL_LOOP
+        for (; x < dw - 2*rx - 4; x += 4) {
+            SkASSERT(prev_x >= 0);
+            SkASSERT(next_x <= sw);
+
+            uint32_t outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            uint32_t inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+            outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+            outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+            outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+
+            prev_x += 4;
+            next_x += 4;
+        }
+#endif
+
+        for (; x < dw - 2*rx; x++) {
+            SkASSERT(prev_x >= 0);
+            SkASSERT(next_x <= sw);
+
+            uint32_t outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
+            uint32_t inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+
+        for (; x < dw; x++) {
+            SkASSERT(prev_x >= 0);
+            SkASSERT(next_x > sw);
+
+            int px = prev_x;
+            int nx = sw;
+
+            int ipx = prev_x + 1;
+            int inx = sw;
+
+            uint32_t outer_sum = sum[px+py] + sum[nx+ny]
+                               - sum[nx+py] - sum[px+ny];
+            uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
+                               - sum[inx+ipy] - sum[ipx+iny];
+            *dst++ = SkToU8((outer_sum * outer_scale
+                           + inner_sum * inner_scale) >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+
+        prev_y += 1;
+        next_y += 1;
+    }
+}
+
+#include "SkColorPriv.h"
+
+static void merge_src_with_blur(uint8_t dst[], int dstRB,
+                                const uint8_t src[], int srcRB,
+                                const uint8_t blur[], int blurRB,
+                                int sw, int sh) {
+    dstRB -= sw;
+    srcRB -= sw;
+    blurRB -= sw;
+    while (--sh >= 0) {
+        for (int x = sw - 1; x >= 0; --x) {
+            *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
+            dst += 1;
+            src += 1;
+            blur += 1;
+        }
+        dst += dstRB;
+        src += srcRB;
+        blur += blurRB;
+    }
+}
+
+static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
+                            const uint8_t src[], int srcRowBytes,
+                            int sw, int sh,
+                            SkBlurMask::Style style) {
+    int x;
+    while (--sh >= 0) {
+        switch (style) {
+        case SkBlurMask::kSolid_Style:
+            for (x = sw - 1; x >= 0; --x) {
+                int s = *src;
+                int d = *dst;
+                *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
+                dst += 1;
+                src += 1;
+            }
+            break;
+        case SkBlurMask::kOuter_Style:
+            for (x = sw - 1; x >= 0; --x) {
+                if (*src) {
+                    *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
+                }
+                dst += 1;
+                src += 1;
+            }
+            break;
+        default:
+            SkDEBUGFAIL("Unexpected blur style here");
+            break;
+        }
+        dst += dstRowBytes - sw;
+        src += srcRowBytes - sw;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// we use a local funciton 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) {
+    SkMask::FreeImage(image);
+}
+
+bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
+                      SkScalar radius, Style style, Quality quality,
+                      SkIPoint* margin)
+{
+    if (src.fFormat != SkMask::kA8_Format) {
+        return false;
+    }
+
+    // Force high quality off for small radii (performance)
+    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 rx = SkScalarCeil(passRadius);
+    int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
+
+    SkASSERT(rx >= 0);
+    SkASSERT((unsigned)outer_weight <= 255);
+    if (rx <= 0) {
+        return false;
+    }
+
+    int ry = rx;    // only do square blur for now
+
+    int padx = passCount * rx;
+    int pady = passCount * ry;
+    if (margin) {
+        margin->set(padx, pady);
+    }
+    dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
+        src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
+    dst->fRowBytes = dst->fBounds.width();
+    dst->fFormat = SkMask::kA8_Format;
+    dst->fImage = NULL;
+
+    if (src.fImage) {
+        size_t dstSize = dst->computeImageSize();
+        if (0 == dstSize) {
+            return false;   // too big to allocate, abort
+        }
+
+        int             sw = src.fBounds.width();
+        int             sh = src.fBounds.height();
+        const uint8_t*  sp = src.fImage;
+        uint8_t*        dp = SkMask::AllocImage(dstSize);
+
+        SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
+
+        // build the blurry destination
+        {
+            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);
+            uint32_t*               sumBuffer = storage.get();
+
+            //pass1: sp is source, dp is destination
+            build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
+            if (outer_weight == 255) {
+                apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
+            } else {
+                apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
+            }
+
+            if (quality == kHigh_Quality) {
+                //pass2: dp is source, tmpBuffer is destination
+                int tmp_sw = sw + 2 * rx;
+                int tmp_sh = sh + 2 * ry;
+                SkAutoTMalloc<uint8_t>  tmpBuffer(dstSize);
+                build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, dp, tmp_sw);
+                if (outer_weight == 255)
+                    apply_kernel(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh);
+                else
+                    apply_kernel_interp(tmpBuffer.get(), rx, ry, sumBuffer,
+                                        tmp_sw, tmp_sh, outer_weight);
+
+                //pass3: tmpBuffer is source, dp is destination
+                tmp_sw += 2 * rx;
+                tmp_sh += 2 * ry;
+                build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, tmpBuffer.get(), tmp_sw);
+                if (outer_weight == 255)
+                    apply_kernel(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh);
+                else
+                    apply_kernel_interp(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh,
+                                        outer_weight);
+            }
+        }
+
+        dst->fImage = dp;
+        // if need be, alloc the "real" dst (same size as src) and copy/merge
+        // the blur into it (applying the src)
+        if (style == kInner_Style) {
+            // now we allocate the "real" dst, mirror the size of src
+            size_t srcSize = src.computeImageSize();
+            if (0 == srcSize) {
+                return false;   // too big to allocate, abort
+            }
+            dst->fImage = SkMask::AllocImage(srcSize);
+            merge_src_with_blur(dst->fImage, src.fRowBytes,
+                                sp, src.fRowBytes,
+                                dp + passCount * (rx + ry * dst->fRowBytes),
+                                dst->fRowBytes, sw, sh);
+            SkMask::FreeImage(dp);
+        } else if (style != kNormal_Style) {
+            clamp_with_orig(dp + passCount * (rx + ry * dst->fRowBytes),
+                            dst->fRowBytes, sp, src.fRowBytes, sw, sh, style);
+        }
+        (void)autoCall.detach();
+    }
+
+    if (style == kInner_Style) {
+        dst->fBounds = src.fBounds; // restore trimmed bounds
+        dst->fRowBytes = src.fRowBytes;
+    }
+
+    return true;
+}
+
diff --git a/legacy/src/effects/SkBlurMask.h b/legacy/src/effects/SkBlurMask.h
new file mode 100644
index 0000000..3709dee
--- /dev/null
+++ b/legacy/src/effects/SkBlurMask.h
@@ -0,0 +1,39 @@
+
+/*
+ * 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 SkBlurMask_DEFINED
+#define SkBlurMask_DEFINED
+
+#include "SkShader.h"
+
+class SkBlurMask {
+public:
+    enum Style {
+        kNormal_Style,  //!< fuzzy inside and outside
+        kSolid_Style,   //!< solid inside, fuzzy outside
+        kOuter_Style,   //!< nothing inside, fuzzy outside
+        kInner_Style,   //!< fuzzy inside, nothing outside
+
+        kStyleCount
+    };
+
+    enum Quality {
+        kLow_Quality,   //!< box blur
+        kHigh_Quality   //!< three pass box blur (similar to gaussian)
+    };
+
+    static bool Blur(SkMask* dst, const SkMask& src,
+                     SkScalar radius, Style style, Quality quality,
+                     SkIPoint* margin = NULL);
+};
+
+#endif
+
+
+
diff --git a/legacy/src/effects/SkBlurMaskFilter.cpp b/legacy/src/effects/SkBlurMaskFilter.cpp
new file mode 100644
index 0000000..fe428d9
--- /dev/null
+++ b/legacy/src/effects/SkBlurMaskFilter.cpp
@@ -0,0 +1,149 @@
+
+/*
+ * 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 "SkBlurMaskFilter.h"
+#include "SkBlurMask.h"
+#include "SkBuffer.h"
+#include "SkMaskFilter.h"
+
+class SkBlurMaskFilterImpl : public SkMaskFilter {
+public:
+    SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle,
+                         uint32_t flags);
+
+    // overrides from SkMaskFilter
+    virtual SkMask::Format getFormat() SK_OVERRIDE;
+    virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
+                            SkIPoint* margin) SK_OVERRIDE;
+    virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE;
+    virtual void computeFastBounds(const SkRect& src, SkRect* dst) SK_OVERRIDE;
+
+    // overrides from SkFlattenable
+    virtual Factory getFactory() SK_OVERRIDE;
+    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+private:
+    SkScalar                    fRadius;
+    SkBlurMaskFilter::BlurStyle fBlurStyle;
+    uint32_t                    fBlurFlags;
+
+    SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
+    
+    typedef SkMaskFilter INHERITED;
+};
+
+SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
+                                       SkBlurMaskFilter::BlurStyle style,
+                                       uint32_t flags) {
+    // use !(radius > 0) instead of radius <= 0 to reject NaN values
+    if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount 
+        || flags > SkBlurMaskFilter::kAll_BlurFlag) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style, flags));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius,
+                                           SkBlurMaskFilter::BlurStyle style,
+                                           uint32_t flags)
+    : fRadius(radius), fBlurStyle(style), fBlurFlags(flags) {
+#if 0
+    fGamma = NULL;
+    if (gammaScale) {
+        fGamma = new U8[256];
+        if (gammaScale > 0)
+            SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
+        else
+            SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
+    }
+#endif
+    SkASSERT(radius >= 0);
+    SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
+    SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
+}
+
+SkMask::Format SkBlurMaskFilterImpl::getFormat() {
+    return SkMask::kA8_Format;
+}
+
+bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
+                                      const SkMatrix& matrix, SkIPoint* margin) {
+    SkScalar radius;
+    if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
+        radius = fRadius;
+    } else {
+        radius = matrix.mapRadius(fRadius);
+    }
+
+    // To avoid unseemly allocation requests (esp. for finite platforms like
+    // handset) we limit the radius so something manageable. (as opposed to
+    // a request like 10,000)
+    static const SkScalar MAX_RADIUS = SkIntToScalar(128);
+    radius = SkMinScalar(radius, MAX_RADIUS);
+    SkBlurMask::Quality blurQuality =
+        (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? 
+            SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
+
+    return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
+                            blurQuality, margin);
+}
+
+void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) {
+    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;
+    SkASSERT(fRadius >= 0);
+    SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
+}
+
+void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fRadius);
+    buffer.write32(fBlurStyle);
+    buffer.write32(fBlurFlags);
+}
+
+static const SkMaskFilter::BlurType gBlurStyle2BlurType[] = {
+    SkMaskFilter::kNormal_BlurType,
+    SkMaskFilter::kSolid_BlurType,
+    SkMaskFilter::kOuter_BlurType,
+    SkMaskFilter::kInner_BlurType,
+};
+
+SkMaskFilter::BlurType SkBlurMaskFilterImpl::asABlur(BlurInfo* info) const {
+    if (info) {
+        info->fRadius = fRadius;
+        info->fIgnoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
+        info->fHighQuality = SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag);
+    }
+    return gBlurStyle2BlurType[fBlurStyle];
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/legacy/src/effects/SkColorFilters.cpp b/legacy/src/effects/SkColorFilters.cpp
new file mode 100644
index 0000000..e6262c1
--- /dev/null
+++ b/legacy/src/effects/SkColorFilters.cpp
@@ -0,0 +1,577 @@
+
+/*
+ * 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 "SkBlitRow.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkUtils.h"
+
+#define ILLEGAL_XFERMODE_MODE   ((SkXfermode::Mode)-1)
+
+// baseclass for filters that store a color and mode
+class SkModeColorFilter : public SkColorFilter {
+public:
+    SkModeColorFilter(SkColor color) {
+        fColor = color;
+        fMode = ILLEGAL_XFERMODE_MODE;
+
+        fPMColor = SkPreMultiplyColor(fColor);
+    }
+
+    SkModeColorFilter(SkColor color, SkXfermode::Mode mode) {
+        fColor = color;
+        fMode = mode;
+
+        fPMColor = SkPreMultiplyColor(fColor);
+    };
+
+    virtual bool asColorMode(SkColor* color, SkXfermode::Mode* mode) {
+        if (ILLEGAL_XFERMODE_MODE == fMode) {
+            return false;
+        }
+
+        if (color) {
+            *color = fColor;
+        }
+        if (mode) {
+            *mode = fMode;
+        }
+        return true;
+    }
+
+    SkColor getColor() const { return fColor; }
+    SkXfermode::Mode getMode() const { return fMode; }
+    bool isModeValid() const { return ILLEGAL_XFERMODE_MODE != fMode; }
+
+protected:
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.write32(fColor);
+        buffer.write32(fMode);
+    }
+
+    SkModeColorFilter(SkFlattenableReadBuffer& buffer) {
+        fColor = buffer.readU32();
+        fMode = (SkXfermode::Mode)buffer.readU32();
+
+        fPMColor = SkPreMultiplyColor(fColor);
+    }
+
+    // cache of fColor in premultiply space
+    SkPMColor   fPMColor;
+
+private:
+    SkColor             fColor;
+    SkXfermode::Mode    fMode;
+
+    typedef SkColorFilter INHERITED;
+};
+
+class Src_SkModeColorFilter : public SkModeColorFilter {
+public:
+    Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {}
+
+    virtual uint32_t getFlags() {
+        if (SkGetPackedA32(fPMColor) == 0xFF) {
+            return kAlphaUnchanged_Flag | kHasFilter16_Flag;
+        } else {
+            return 0;
+        }
+    }
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) {
+        sk_memset32(result, fPMColor, count);
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]) {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+        sk_memset16(result, SkPixel32ToPixel16(fPMColor), count);
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(Src_SkModeColorFilter, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    Src_SkModeColorFilter(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+private:
+    typedef SkModeColorFilter INHERITED;
+};
+
+class SrcOver_SkModeColorFilter : public SkModeColorFilter {
+public:
+    SrcOver_SkModeColorFilter(SkColor color)
+            : INHERITED(color, SkXfermode::kSrcOver_Mode) {
+        fColor32Proc = NULL;
+    }
+
+    virtual uint32_t getFlags() {
+        if (SkGetPackedA32(fPMColor) == 0xFF) {
+            return kAlphaUnchanged_Flag | kHasFilter16_Flag;
+        } else {
+            return 0;
+        }
+    }
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) {
+        if (NULL == fColor32Proc) {
+            fColor32Proc = SkBlitRow::ColorProcFactory();
+        }
+        fColor32Proc(result, shader, count, fPMColor);
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]) {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+        sk_memset16(result, SkPixel32ToPixel16(fPMColor), count);
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SrcOver_SkModeColorFilter, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc;  }
+
+    SrcOver_SkModeColorFilter(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer), fColor32Proc(NULL) {}
+
+private:
+
+    SkBlitRow::ColorProc fColor32Proc;
+
+    typedef SkModeColorFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+
+    // first collaps some modes if possible
+
+    if (SkXfermode::kClear_Mode == mode) {
+        color = 0;
+        mode = SkXfermode::kSrc_Mode;
+    } else if (SkXfermode::kSrcOver_Mode == mode) {
+        if (0 == alpha) {
+            mode = SkXfermode::kDst_Mode;
+        } else if (255 == alpha) {
+            mode = SkXfermode::kSrc_Mode;
+        }
+        // else just stay srcover
+    }
+
+    // weed out combinations that are noops, and just return null
+    if (SkXfermode::kDst_Mode == mode ||
+        (0 == alpha && (SkXfermode::kSrcOver_Mode == mode ||
+                        SkXfermode::kDstOver_Mode == mode ||
+                        SkXfermode::kDstOut_Mode == mode ||
+                        SkXfermode::kSrcATop_Mode == mode ||
+                        SkXfermode::kXor_Mode == mode ||
+                        SkXfermode::kDarken_Mode == mode)) ||
+            (0xFF == alpha && SkXfermode::kDstIn_Mode == mode)) {
+        return NULL;
+    }
+
+    switch (mode) {
+        case SkXfermode::kSrc_Mode:
+            return SkNEW_ARGS(Src_SkModeColorFilter, (color));
+        case SkXfermode::kSrcOver_Mode:
+            return SkNEW_ARGS(SrcOver_SkModeColorFilter, (color));
+        default:
+            return SkNEW_ARGS(Proc_SkModeColorFilter, (color, mode));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline unsigned pin(unsigned value, unsigned max) {
+    if (value > max) {
+        value = max;
+    }
+    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[]) {
+        unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
+        unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
+        unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
+
+        unsigned addR = SkColorGetR(fAdd);
+        unsigned addG = SkColorGetG(fAdd);
+        unsigned addB = SkColorGetB(fAdd);
+
+        for (int i = 0; i < count; i++) {
+            SkPMColor c = shader[i];
+            if (c) {
+                unsigned a = SkGetPackedA32(c);
+                unsigned scaleA = SkAlpha255To256(a);
+                unsigned r = pin(SkAlphaMul(SkGetPackedR32(c), scaleR) + SkAlphaMul(addR, scaleA), a);
+                unsigned g = pin(SkAlphaMul(SkGetPackedG32(c), scaleG) + SkAlphaMul(addG, scaleA), a);
+                unsigned b = pin(SkAlphaMul(SkGetPackedB32(c), scaleB) + SkAlphaMul(addB, scaleA), a);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkLightingColorFilter, (buffer));
+    }
+
+protected:
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.write32(fMul);
+        buffer.write32(fAdd);
+    }
+
+    virtual Factory getFactory() {
+        return CreateProc;
+    }
+
+    SkLightingColorFilter(SkFlattenableReadBuffer& buffer) {
+        fMul = buffer.readU32();
+        fAdd = buffer.readU32();
+    }
+
+    SkColor fMul, fAdd;
+
+private:
+    typedef SkColorFilter INHERITED;
+};
+
+class SkLightingColorFilter_JustAdd : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_JustAdd(SkColor mul, SkColor add)
+        : INHERITED(mul, add) {}
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) {
+        unsigned addR = SkColorGetR(fAdd);
+        unsigned addG = SkColorGetG(fAdd);
+        unsigned addB = SkColorGetB(fAdd);
+
+        for (int i = 0; i < count; i++) {
+            SkPMColor c = shader[i];
+            if (c) {
+                unsigned a = SkGetPackedA32(c);
+                unsigned scaleA = SkAlpha255To256(a);
+                unsigned r = pin(SkGetPackedR32(c) + SkAlphaMul(addR, scaleA), a);
+                unsigned g = pin(SkGetPackedG32(c) + SkAlphaMul(addG, scaleA), a);
+                unsigned b = pin(SkGetPackedB32(c) + SkAlphaMul(addB, scaleA), a);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)  {
+        return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkLightingColorFilter_JustAdd(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+private:
+    typedef SkLightingColorFilter INHERITED;
+};
+
+class SkLightingColorFilter_JustMul : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_JustMul(SkColor mul, SkColor add)
+        : INHERITED(mul, add) {}
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) {
+        unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
+        unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
+        unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
+
+        for (int i = 0; i < count; i++) {
+            SkPMColor c = shader[i];
+            if (c) {
+                unsigned a = SkGetPackedA32(c);
+                unsigned r = SkAlphaMul(SkGetPackedR32(c), scaleR);
+                unsigned g = SkAlphaMul(SkGetPackedG32(c), scaleG);
+                unsigned b = SkAlphaMul(SkGetPackedB32(c), scaleB);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkLightingColorFilter_JustMul, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkLightingColorFilter_JustMul(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+private:
+    typedef SkLightingColorFilter INHERITED;
+};
+
+class SkLightingColorFilter_SingleMul : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_SingleMul(SkColor mul, SkColor add)
+            : INHERITED(mul, add) {
+        SkASSERT(SkColorGetR(add) == 0);
+        SkASSERT(SkColorGetG(add) == 0);
+        SkASSERT(SkColorGetB(add) == 0);
+        SkASSERT(SkColorGetR(mul) == SkColorGetG(mul));
+        SkASSERT(SkColorGetR(mul) == SkColorGetB(mul));
+    }
+
+    virtual uint32_t getFlags() {
+        return this->INHERITED::getFlags() | (kAlphaUnchanged_Flag | kHasFilter16_Flag);
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]) {
+        // all mul components are the same
+        unsigned scale = SkAlpha255To256(SkColorGetR(fMul));
+
+        if (count > 0) {
+            do {
+                *result++ = SkAlphaMulRGB16(*shader++, scale);
+            } while (--count > 0);
+        }
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkLightingColorFilter_SingleMul, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkLightingColorFilter_SingleMul(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+private:
+    typedef SkLightingColorFilter INHERITED;
+};
+
+class SkLightingColorFilter_NoPin : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_NoPin(SkColor mul, SkColor add)
+    : INHERITED(mul, add) {}
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) {
+        unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
+        unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
+        unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
+
+        unsigned addR = SkColorGetR(fAdd);
+        unsigned addG = SkColorGetG(fAdd);
+        unsigned addB = SkColorGetB(fAdd);
+
+        for (int i = 0; i < count; i++) {
+            SkPMColor c = shader[i];
+            if (c) {
+                unsigned a = SkGetPackedA32(c);
+                unsigned scaleA = SkAlpha255To256(a);
+                unsigned r = SkAlphaMul(SkGetPackedR32(c), scaleR) + SkAlphaMul(addR, scaleA);
+                unsigned g = SkAlphaMul(SkGetPackedG32(c), scaleG) + SkAlphaMul(addG, scaleA);
+                unsigned b = SkAlphaMul(SkGetPackedB32(c), scaleB) + SkAlphaMul(addB, scaleA);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkLightingColorFilter_NoPin, (buffer));
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkLightingColorFilter_NoPin(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+private:
+    typedef SkLightingColorFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkSimpleColorFilter : public SkColorFilter {
+public:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW(SkSimpleColorFilter);
+    }
+
+protected:
+    void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+        if (result != src) {
+            memcpy(result, src, count * sizeof(SkPMColor));
+        }
+    }
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {}
+
+    virtual Factory getFactory() {
+        return CreateProc;
+    }
+
+};
+
+SkColorFilter* SkColorFilter::CreateLightingFilter(SkColor mul, SkColor add) {
+    mul &= 0x00FFFFFF;
+    add &= 0x00FFFFFF;
+
+    if (0xFFFFFF == mul) {
+        if (0 == add) {
+            return SkNEW(SkSimpleColorFilter);   // no change to the colors
+        } else {
+            return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (mul, add));
+        }
+    }
+
+    if (0 == add) {
+        if (SkColorGetR(mul) == SkColorGetG(mul) &&
+                SkColorGetR(mul) == SkColorGetB(mul)) {
+            return SkNEW_ARGS(SkLightingColorFilter_SingleMul, (mul, add));
+        } else {
+            return SkNEW_ARGS(SkLightingColorFilter_JustMul, (mul, add));
+        }
+    }
+
+    if (SkColorGetR(mul) + SkColorGetR(add) <= 255 &&
+        SkColorGetG(mul) + SkColorGetG(add) <= 255 &&
+        SkColorGetB(mul) + SkColorGetB(add) <= 255) {
+            return SkNEW_ARGS(SkLightingColorFilter_NoPin, (mul, add));
+    }
+
+    return SkNEW_ARGS(SkLightingColorFilter, (mul, add));
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkColorFilter)
+    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)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingColorFilter_SingleMul)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingColorFilter_NoPin)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSimpleColorFilter)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
+
diff --git a/legacy/src/effects/SkColorMatrixFilter.cpp b/legacy/src/effects/SkColorMatrixFilter.cpp
new file mode 100644
index 0000000..79fbea5
--- /dev/null
+++ b/legacy/src/effects/SkColorMatrixFilter.cpp
@@ -0,0 +1,359 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkColorMatrixFilter.h"
+#include "SkColorMatrix.h"
+#include "SkColorPriv.h"
+#include "SkUnPreMultiply.h"
+
+static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
+                          unsigned b, unsigned a) {
+    return array[0] * r + array[1] * g  + array[2] * b + array[3] * a + array[4];
+}
+
+static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
+                       unsigned b) {
+    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;
+
+    result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
+    result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
+    result[2] = rowmul4(&array[10], r, g, b, a) >> shift;
+    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;
+
+    result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
+    result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
+    result[2] = rowmul4(&array[10], r, g, b, a) >> 16;
+    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;
+
+    result[0] = rowmul3(&array[0], r, g, b) >> shift;
+    result[1] = rowmul3(&array[5], r, g, b) >> shift;
+    result[2] = rowmul3(&array[10], r, g, b) >> shift;
+    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;
+
+    result[0] = rowmul3(&array[0], r, g, b) >> 16;
+    result[1] = rowmul3(&array[5], r, g, b) >> 16;
+    result[2] = rowmul3(&array[10], r, g, b) >> 16;
+    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;
+
+    // cast to (int) to keep the expression signed for the shift
+    result[0] = (array[0] * (int)r + array[4]) >> shift;
+    result[1] = (array[6] * (int)g + array[9]) >> shift;
+    result[2] = (array[12] * (int)b + array[14]) >> shift;
+    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;
+
+    // cast to (int) to keep the expression signed for the shift
+    result[0] = (array[0] * (int)r + array[4]) >> 16;
+    result[1] = (array[6] * (int)g + array[9]) >> 16;
+    result[2] = (array[12] * (int)b + array[14]) >> 16;
+    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;
+
+    result[0] = r + (array[4] >> shift);
+    result[1] = g + (array[9] >> shift);
+    result[2] = b + (array[14] >> shift);
+    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;
+
+    result[0] = r + (array[4] >> 16);
+    result[1] = g + (array[9] >> 16);
+    result[2] = b + (array[14] >> 16);
+    result[3] = a;
+}
+
+#define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag |  \
+                         SkColorFilter::kHasFilter16_Flag)
+
+// src is [20] but some compilers won't accept __restrict__ on anything
+// but an raw pointer or reference
+void SkColorMatrixFilter::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;
+    }
+
+    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;
+        value = SkAbs32(value);
+        max = SkMax32(max, value);
+    }
+
+    /*  All of fArray[] values must fit in 23 bits, to safely allow me to
+        multiply them by 8bit unsigned values, and get a signed answer without
+        overflow. This means clz needs to be 9 or bigger
+    */
+    int bits = SkCLZ(max);
+    int32_t one = SK_Fixed1;
+
+    fState.fShift = 16; // we are starting out as fixed 16.16
+    if (bits < 9) {
+        bits = 9 - bits;
+        fState.fShift -= bits;
+        for (i = 0; i < 20; i++) {
+            array[i] >>= bits;
+        }
+        one >>= bits;
+    }
+
+    // check if we have to munge Alpha
+    int32_t changesAlpha = (array[15] | array[16] | array[17] |
+                            (array[18] - one) | array[19]);
+    int32_t usesAlpha = (array[3] | array[8] | array[13]);
+    bool shiftIs16 = (16 == fState.fShift);
+
+    if (changesAlpha | usesAlpha) {
+        fProc = shiftIs16 ? General16 : General;
+        fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
+    } else {
+        fFlags = kNO_ALPHA_FLAGS;
+
+        int32_t needsScale = (array[0] - one) |       // red axis
+                             (array[6] - one) |       // green axis
+                             (array[12] - one);       // blue axis
+
+        int32_t needs3x3 =  array[1] | array[2] |     // red off-axis
+                            array[5] | array[7] |     // green off-axis
+                            array[10] | array[11];    // blue off-axis
+
+        if (needs3x3) {
+            fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
+        } else if (needsScale) {
+            fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
+        } else if (array[4] | array[9] | array[14]) {   // needs add
+            fProc = shiftIs16 ? Add16 : Add;
+        } else {
+            fProc = NULL;   // identity
+        }
+    }
+
+    /*  preround our add values so we get a rounded shift. We do this after we
+        analyze the array, so we don't miss the case where the caller has zeros
+        which could make us accidentally take the General or Add case.
+    */
+    if (NULL != fProc) {
+        int32_t add = 1 << (fState.fShift - 1);
+        array[4] += add;
+        array[9] += add;
+        array[14] += add;
+        array[19] += add;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int32_t pin(int32_t value, int32_t max) {
+    if (value < 0) {
+        value = 0;
+    }
+    if (value > max) {
+        value = max;
+    }
+    return value;
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter() {
+    this->setup(NULL);
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) {
+    this->setup(cm.fMat);
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
+    this->setup(array);
+}
+
+uint32_t SkColorMatrixFilter::getFlags() {
+    return this->INHERITED::getFlags() | fFlags;
+}
+
+void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
+                                     SkPMColor dst[]) {
+    Proc proc = fProc;
+    State* state = &fState;
+    int32_t* result = state->fResult;
+
+    if (NULL == proc) {
+        if (src != dst) {
+            memcpy(dst, src, count * sizeof(SkPMColor));
+        }
+        return;
+    }
+
+    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
+
+    for (int i = 0; i < count; i++) {
+        SkPMColor c = src[i];
+
+        unsigned r = SkGetPackedR32(c);
+        unsigned g = SkGetPackedG32(c);
+        unsigned b = SkGetPackedB32(c);
+        unsigned a = SkGetPackedA32(c);
+
+        // need our components to be un-premultiplied
+        if (255 != a) {
+            SkUnPreMultiply::Scale scale = table[a];
+            r = SkUnPreMultiply::ApplyScale(scale, r);
+            g = SkUnPreMultiply::ApplyScale(scale, g);
+            b = SkUnPreMultiply::ApplyScale(scale, b);
+
+            SkASSERT(r <= 255);
+            SkASSERT(g <= 255);
+            SkASSERT(b <= 255);
+        }
+
+        proc(state, r, g, b, a);
+
+        r = pin(result[0], SK_R32_MASK);
+        g = pin(result[1], SK_G32_MASK);
+        b = pin(result[2], SK_B32_MASK);
+        a = pin(result[3], SK_A32_MASK);
+        // re-prepremultiply if needed
+        dst[i] = SkPremultiplyARGBInline(a, r, g, b);
+    }
+}
+
+void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
+                                       uint16_t dst[]) {
+    SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
+
+    Proc   proc = fProc;
+    State* state = &fState;
+    int32_t* result = state->fResult;
+
+    if (NULL == proc) {
+        if (src != dst) {
+            memcpy(dst, src, count * sizeof(uint16_t));
+        }
+        return;
+    }
+
+    for (int i = 0; i < count; i++) {
+        uint16_t c = src[i];
+
+        // expand to 8bit components (since our matrix translate is 8bit biased
+        unsigned r = SkPacked16ToR32(c);
+        unsigned g = SkPacked16ToG32(c);
+        unsigned b = SkPacked16ToB32(c);
+
+        proc(state, r, g, b, 0);
+
+        r = pin(result[0], SK_R32_MASK);
+        g = pin(result[1], SK_G32_MASK);
+        b = pin(result[2], SK_B32_MASK);
+
+        // now packed it back down to 16bits (hmmm, could dither...)
+        dst[i] = SkPack888ToRGB16(r, g, b);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer)  {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeFunctionPtr((void*)fProc);
+    buffer.writeMul4(&fState, sizeof(fState));
+    buffer.write32(fFlags);
+}
+
+SkFlattenable::Factory SkColorMatrixFilter::getFactory() { return CreateProc;  }
+
+SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fProc = (Proc)buffer.readFunctionPtr();
+    buffer.read(&fState, sizeof(fState));
+    fFlags = buffer.readU32();
+}
+
+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);
+    }
+    return true;
+}
+
+SkFlattenable* SkColorMatrixFilter::CreateProc(SkFlattenableReadBuffer& buf) {
+    return SkNEW_ARGS(SkColorMatrixFilter, (buf));
+}
+
+void SkColorMatrixFilter::setMatrix(const SkColorMatrix& matrix) {
+    setup(matrix.fMat);
+}
+
+void SkColorMatrixFilter::setArray(const SkScalar array[20]) {
+    setup(array);
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkColorMatrixFilter)
diff --git a/legacy/src/effects/SkCornerPathEffect.cpp b/legacy/src/effects/SkCornerPathEffect.cpp
new file mode 100644
index 0000000..4da31ab
--- /dev/null
+++ b/legacy/src/effects/SkCornerPathEffect.cpp
@@ -0,0 +1,149 @@
+
+/*
+ * 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 "SkCornerPathEffect.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkBuffer.h"
+
+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;
+    } else {
+        step->scale(SkScalarDiv(radius, dist));
+        return true;
+    }
+}
+
+bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                    SkScalar* width) {
+    if (fRadius == 0) {
+        return false;
+    }
+
+    SkPath::Iter    iter(src, false);
+    SkPath::Verb    verb, prevVerb = (SkPath::Verb)-1;
+    SkPoint         pts[4];
+
+    bool        closed;
+    SkPoint     moveTo, lastCorner;
+    SkVector    firstStep, step;
+    bool        prevIsValid = true;
+
+    // to avoid warnings
+    moveTo.set(0, 0);
+    firstStep.set(0, 0);
+    lastCorner.set(0, 0);
+
+    for (;;) {
+        switch (verb = iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                    // close out the previous (open) contour
+                if (SkPath::kLine_Verb == prevVerb) {
+                    dst->lineTo(lastCorner);
+                }
+                closed = iter.isClosedContour();
+                if (closed) {
+                    moveTo = pts[0];
+                    prevIsValid = false;
+                } else {
+                    dst->moveTo(pts[0]);
+                    prevIsValid = true;
+                }
+                break;
+            case SkPath::kLine_Verb: {
+                bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
+                // prev corner
+                if (!prevIsValid) {
+                    dst->moveTo(moveTo + step);
+                    prevIsValid = true;
+                } else {
+                    dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX,
+                                pts[0].fY + step.fY);
+                }
+                if (drawSegment) {
+                    dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
+                }
+                lastCorner = pts[1];
+                prevIsValid = true;
+                break;
+            }
+            case SkPath::kQuad_Verb:
+                // TBD - just replicate the curve for now
+                if (!prevIsValid) {
+                    dst->moveTo(pts[0]);
+                    prevIsValid = true;
+                }
+                dst->quadTo(pts[1], pts[2]);
+                lastCorner = pts[2];
+                firstStep.set(0, 0);
+                break;
+            case SkPath::kCubic_Verb:
+                if (!prevIsValid) {
+                    dst->moveTo(pts[0]);
+                    prevIsValid = true;
+                }
+                // TBD - just replicate the curve for now
+                dst->cubicTo(pts[1], pts[2], pts[3]);
+                lastCorner = pts[3];
+                firstStep.set(0, 0);
+                break;
+            case SkPath::kClose_Verb:
+                if (firstStep.fX || firstStep.fY) {
+                    dst->quadTo(lastCorner.fX, lastCorner.fY,
+                                lastCorner.fX + firstStep.fX,
+                                lastCorner.fY + firstStep.fY);
+                    }
+                dst->close();
+                break;
+            case SkPath::kDone_Verb:
+                goto DONE;
+        }
+
+        if (SkPath::kMove_Verb == prevVerb) {
+            firstStep = step;
+        }
+        prevVerb = verb;
+    }
+DONE:
+    return true;
+}
+
+SkFlattenable::Factory SkCornerPathEffect::getFactory() {
+    return CreateProc;
+}
+
+void SkCornerPathEffect::flatten(SkFlattenableWriteBuffer& 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/legacy/src/effects/SkDashPathEffect.cpp b/legacy/src/effects/SkDashPathEffect.cpp
new file mode 100644
index 0000000..7950d64
--- /dev/null
+++ b/legacy/src/effects/SkDashPathEffect.cpp
@@ -0,0 +1,171 @@
+
+/*
+ * 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 "SkDashPathEffect.h"
+#include "SkBuffer.h"
+#include "SkPathMeasure.h"
+
+static inline int is_even(int x) {
+    return (~x) << 31;
+}
+
+static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
+                                  int32_t* index) {
+    int i;
+
+    for (i = 0; phase > intervals[i]; i++) {
+        phase -= intervals[i];
+    }
+    *index = i;
+    return intervals[i] - phase;
+}
+
+SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
+                                   SkScalar phase, bool scaleToFit)
+        : fScaleToFit(scaleToFit) {
+    SkASSERT(intervals);
+    SkASSERT(count > 1 && SkAlign2(count) == count);
+
+    fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
+    fCount = count;
+
+    SkScalar len = 0;
+    for (int i = 0; i < count; i++) {
+        SkASSERT(intervals[i] >= 0);
+        fIntervals[i] = intervals[i];
+        len += intervals[i];
+    }
+    fIntervalLength = len;
+
+    if (len > 0) {  // we don't handle 0 length dash arrays
+        if (phase < 0) {
+            phase = -phase;
+            if (phase > len) {
+                phase = SkScalarMod(phase, len);
+            }
+            phase = len - phase;
+        } 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);
+
+        SkASSERT(fInitialDashLength >= 0);
+        SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
+    } else {
+        BAD_DASH:
+        fInitialDashLength = -1;    // signal bad dash intervals
+    }
+}
+
+SkDashPathEffect::~SkDashPathEffect() {
+    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) {
+        return false;
+    }
+
+    SkPathMeasure   meas(src, false);
+    const SkScalar* intervals = fIntervals;
+
+    do {
+        bool        skipFirstSegment = meas.isClosed();
+        bool        addedSegment = false;
+        SkScalar    length = meas.getLength();
+        int         index = fInitialDashIndex;
+        SkScalar    scale = SK_Scalar1;
+
+        if (fScaleToFit) {
+            if (fIntervalLength >= length) {
+                scale = SkScalarDiv(length, fIntervalLength);
+            } else {
+                SkScalar div = SkScalarDiv(length, fIntervalLength);
+                int n = SkScalarFloor(div);
+                scale = SkScalarDiv(length, n * fIntervalLength);
+            }
+        }
+
+        SkScalar    distance = 0;
+        SkScalar    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);
+            }
+            distance += dlen;
+
+            // clear this so we only respect it the first time around
+            skipFirstSegment = false;
+
+            // wrap around our intervals array if necessary
+            index += 1;
+            SkASSERT(index <= fCount);
+            if (index == fCount) {
+                index = 0;
+            }
+
+            // fetch our next dlen
+            dlen = SkScalarMul(intervals[index], scale);
+        }
+
+        // extend if we ended on a segment and we need to join up with the (skipped) initial segment
+        if (meas.isClosed() && is_even(fInitialDashIndex) &&
+                fInitialDashLength > 0) {
+            meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
+        }
+    } while (meas.nextContour());
+    return true;
+}
+
+SkFlattenable::Factory SkDashPathEffect::getFactory() {
+    return fInitialDashLength < 0 ? NULL : CreateProc;
+}
+
+void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+    SkASSERT(fInitialDashLength >= 0);
+
+    buffer.write32(fCount);
+    buffer.write32(fInitialDashIndex);
+    buffer.writeScalar(fInitialDashLength);
+    buffer.writeScalar(fIntervalLength);
+    buffer.write32(fScaleToFit);
+    buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
+}
+
+SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkDashPathEffect, (buffer));
+}
+
+SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
+    fCount = buffer.readS32();
+    fInitialDashIndex = buffer.readS32();
+    fInitialDashLength = buffer.readScalar();
+    fIntervalLength = buffer.readScalar();
+    fScaleToFit = (buffer.readS32() != 0);
+    
+    fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
+    buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkDashPathEffect)
diff --git a/legacy/src/effects/SkDiscretePathEffect.cpp b/legacy/src/effects/SkDiscretePathEffect.cpp
new file mode 100644
index 0000000..a438859
--- /dev/null
+++ b/legacy/src/effects/SkDiscretePathEffect.cpp
@@ -0,0 +1,91 @@
+
+/*
+ * 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 "SkDiscretePathEffect.h"
+#include "SkBuffer.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+
+static void Perterb(SkPoint* p, const SkVector& tangent, SkScalar scale) {
+    SkVector normal = tangent;
+    normal.rotateCCW();
+    normal.setLength(scale);
+    *p += normal;
+}
+
+
+SkDiscretePathEffect::SkDiscretePathEffect(SkScalar segLength, SkScalar deviation)
+    : fSegLength(segLength), fPerterb(deviation)
+{
+}
+
+bool SkDiscretePathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                      SkScalar* width) {
+    bool doFill = *width < 0;
+
+    SkPathMeasure   meas(src, doFill);
+    uint32_t        seed = SkScalarRound(meas.getLength());
+    SkRandom        rand(seed ^ ((seed << 16) | (seed >> 16)));
+    SkScalar        scale = fPerterb;
+    SkPoint         p;
+    SkVector        v;
+
+    do {
+        SkScalar    length = meas.getLength();
+
+        if (fSegLength * (2 + doFill) > length) {
+            meas.getSegment(0, length, dst, true);  // to short for us to mangle
+        } else {
+            int         n = SkScalarRound(SkScalarDiv(length, fSegLength));
+            SkScalar    delta = length / n;
+            SkScalar    distance = 0;
+
+            if (meas.isClosed()) {
+                n -= 1;
+                distance += delta/2;
+            }
+            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.isClosed()) {
+                dst->close();
+            }
+        }
+    } while (meas.nextContour());
+    return true;
+}
+
+SkFlattenable::Factory SkDiscretePathEffect::getFactory() {
+    return CreateProc;
+}
+
+SkFlattenable* SkDiscretePathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkDiscretePathEffect, (buffer));
+}
+
+void SkDiscretePathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+    buffer.writeScalar(fSegLength);
+    buffer.writeScalar(fPerterb);
+}
+
+SkDiscretePathEffect::SkDiscretePathEffect(SkFlattenableReadBuffer& buffer) {
+    fSegLength = buffer.readScalar();
+    fPerterb = buffer.readScalar();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkDiscretePathEffect)
+
diff --git a/src/effects/SkEffects.cpp b/legacy/src/effects/SkEffects.cpp
similarity index 100%
rename from src/effects/SkEffects.cpp
rename to legacy/src/effects/SkEffects.cpp
diff --git a/src/effects/SkEffects_none.cpp b/legacy/src/effects/SkEffects_none.cpp
similarity index 100%
rename from src/effects/SkEffects_none.cpp
rename to legacy/src/effects/SkEffects_none.cpp
diff --git a/legacy/src/effects/SkEmbossMask.cpp b/legacy/src/effects/SkEmbossMask.cpp
new file mode 100644
index 0000000..300494b
--- /dev/null
+++ b/legacy/src/effects/SkEmbossMask.cpp
@@ -0,0 +1,165 @@
+
+/*
+ * 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 "SkEmbossMask.h"
+#include "SkMath.h"
+
+static inline int nonzero_to_one(int x) {
+#if 0
+    return x != 0;
+#else
+    return ((unsigned)(x | -x)) >> 31;
+#endif
+}
+
+static inline int neq_to_one(int x, int max) {
+#if 0
+    return x != max;
+#else
+    SkASSERT(x >= 0 && x <= max);
+    return ((unsigned)(x - max)) >> 31;
+#endif
+}
+
+static inline int neq_to_mask(int x, int max) {
+#if 0
+    return -(x != max);
+#else
+    SkASSERT(x >= 0 && x <= max);
+    return (x - max) >> 31;
+#endif
+}
+
+static inline unsigned div255(unsigned x) {
+    SkASSERT(x <= (255*255));
+    return x * ((1 << 24) / 255) >> 24;
+}
+
+#define kDelta  32  // small enough to show off angle differences
+
+#include "SkEmbossMask_Table.h"
+
+#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
+
+#include <stdio.h>
+
+void SkEmbossMask_BuildTable() {
+    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
+
+    FILE* file = ::fopen("SkEmbossMask_Table.h", "w");
+    SkASSERT(file);
+    ::fprintf(file, "#include \"SkTypes.h\"\n\n");
+    ::fprintf(file, "static const U16 gInvSqrtTable[128 * 128] = {\n");
+    for (int dx = 0; dx <= 255/2; dx++) {
+        for (int dy = 0; dy <= 255/2; dy++) {
+            if ((dy & 15) == 0)
+                ::fprintf(file, "\t");
+
+            uint16_t value = SkToU16((1 << 15) / SkSqrt32(dx * dx + dy * dy + kDelta*kDelta/4));
+
+            ::fprintf(file, "0x%04X", value);
+            if (dx * 128 + dy < 128*128-1) {
+                ::fprintf(file, ", ");
+            }
+            if ((dy & 15) == 15) {
+                ::fprintf(file, "\n");
+            }
+        }
+    }
+    ::fprintf(file, "};\n#define kDeltaUsedToBuildTable\t%d\n", kDelta);
+    ::fclose(file);
+}
+
+#endif
+
+void SkEmbossMask::Emboss(SkMask* mask, const SkEmbossMaskFilter::Light& light) {
+    SkASSERT(kDelta == kDeltaUsedToBuildTable);
+
+    SkASSERT(mask->fFormat == SkMask::k3D_Format);
+
+    int     specular = light.fSpecular;
+    int     ambient = light.fAmbient;
+    SkFixed lx = SkScalarToFixed(light.fDirection[0]);
+    SkFixed ly = SkScalarToFixed(light.fDirection[1]);
+    SkFixed lz = SkScalarToFixed(light.fDirection[2]);
+    SkFixed lz_dot_nz = lz * kDelta;
+    int     lz_dot8 = lz >> 8;
+
+    size_t      planeSize = mask->computeImageSize();
+    uint8_t*    alpha = mask->fImage;
+    uint8_t*    multiply = (uint8_t*)alpha + planeSize;
+    uint8_t*    additive = multiply + planeSize;
+
+    int rowBytes = mask->fRowBytes;
+    int maxy = mask->fBounds.height() - 1;
+    int maxx = mask->fBounds.width() - 1;
+
+    int prev_row = 0;
+    for (int y = 0; y <= maxy; y++) {
+        int next_row = neq_to_mask(y, maxy) & rowBytes;
+
+        for (int x = 0; x <= maxx; x++) {
+            if (alpha[x]) {
+                int nx = alpha[x + neq_to_one(x, maxx)] - alpha[x - nonzero_to_one(x)];
+                int ny = alpha[x + next_row] - alpha[x - prev_row];
+
+                SkFixed numer = lx * nx + ly * ny + lz_dot_nz;
+                int     mul = ambient;
+                int     add = 0;
+
+                if (numer > 0) {  // preflight when numer/denom will be <= 0
+#if 0
+                    int denom = SkSqrt32(nx * nx + ny * ny + kDelta*kDelta);
+                    SkFixed dot = numer / denom;
+                    dot >>= 8;  // now dot is 2^8 instead of 2^16
+#else
+                    // can use full numer, but then we need to call SkFixedMul, since
+                    // numer is 24 bits, and our table is 12 bits
+
+                    // SkFixed dot = SkFixedMul(numer, gTable[]) >> 8
+                    SkFixed dot = (unsigned)(numer >> 4) * gInvSqrtTable[(SkAbs32(nx) >> 1 << 7) | (SkAbs32(ny) >> 1)] >> 20;
+#endif
+                    mul = SkFastMin32(mul + dot, 255);
+
+                    // now for the reflection
+
+                    //  R = 2 (Light * Normal) Normal - Light
+                    //  hilite = R * Eye(0, 0, 1)
+
+                    int hilite = (2 * dot - lz_dot8) * lz_dot8 >> 8;
+                    if (hilite > 0) {
+                        // pin hilite to 255, since our fast math is also a little sloppy
+                        hilite = SkClampMax(hilite, 255);
+
+                        // specular is 4.4
+                        // would really like to compute the fractional part of this
+                        // and then possibly cache a 256 table for a given specular
+                        // value in the light, and just pass that in to this function.
+                        add = hilite;
+                        for (int i = specular >> 4; i > 0; --i) {
+                            add = div255(add * hilite);
+                        }
+                    }
+                }
+                multiply[x] = SkToU8(mul);
+                additive[x] = SkToU8(add);
+
+            //  multiply[x] = 0xFF;
+            //  additive[x] = 0;
+            //  ((uint8_t*)alpha)[x] = alpha[x] * multiply[x] >> 8;
+            }
+        }
+        alpha += rowBytes;
+        multiply += rowBytes;
+        additive += rowBytes;
+        prev_row = rowBytes;
+    }
+}
+
+
diff --git a/legacy/src/effects/SkEmbossMask.h b/legacy/src/effects/SkEmbossMask.h
new file mode 100644
index 0000000..15e2474
--- /dev/null
+++ b/legacy/src/effects/SkEmbossMask.h
@@ -0,0 +1,21 @@
+
+/*
+ * 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 SkEmbossMask_DEFINED
+#define SkEmbossMask_DEFINED
+
+#include "SkEmbossMaskFilter.h"
+
+class SkEmbossMask {
+public:
+    static void Emboss(SkMask* mask, const SkEmbossMaskFilter::Light&);
+};
+
+#endif
+
diff --git a/legacy/src/effects/SkEmbossMaskFilter.cpp b/legacy/src/effects/SkEmbossMaskFilter.cpp
new file mode 100644
index 0000000..ce37718
--- /dev/null
+++ b/legacy/src/effects/SkEmbossMaskFilter.cpp
@@ -0,0 +1,140 @@
+
+/*
+ * 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 "SkEmbossMaskFilter.h"
+#include "SkBlurMaskFilter.h"
+#include "SkBlurMask.h"
+#include "SkEmbossMask.h"
+#include "SkBuffer.h"
+
+static inline int pin2byte(int n) {
+    if (n < 0) {
+        n = 0;
+    } else if (n > 0xFF) {
+        n = 0xFF;
+    }
+    return n;
+}
+
+SkMaskFilter* SkBlurMaskFilter::CreateEmboss(const SkScalar direction[3],
+                                             SkScalar ambient, SkScalar specular,
+                                             SkScalar blurRadius) {
+    if (direction == NULL) {
+        return NULL;
+    }
+
+    // ambient should be 0...1 as a scalar
+    int am = pin2byte(SkScalarToFixed(ambient) >> 8);
+
+    // specular should be 0..15.99 as a scalar
+    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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void normalize(SkScalar v[3]) {
+    SkScalar mag = SkScalarSquare(v[0]) + SkScalarSquare(v[1]) + SkScalarSquare(v[2]);
+    mag = SkScalarSqrt(mag);
+
+    for (int i = 0; i < 3; i++) {
+        v[i] = SkScalarDiv(v[i], mag);
+    }
+}
+
+SkEmbossMaskFilter::SkEmbossMaskFilter(const Light& light, SkScalar blurRadius)
+        : fLight(light), fBlurRadius(blurRadius) {
+    normalize(fLight.fDirection);
+}
+
+SkMask::Format SkEmbossMaskFilter::getFormat() {
+    return SkMask::k3D_Format;
+}
+
+bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src,
+                                    const SkMatrix& matrix, SkIPoint* margin) {
+    SkScalar radius = matrix.mapRadius(fBlurRadius);
+
+    if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style,
+                          SkBlurMask::kLow_Quality)) {
+        return false;
+    }
+
+    dst->fFormat = SkMask::k3D_Format;
+    if (margin) {
+        margin->set(SkScalarCeil(radius), SkScalarCeil(radius));
+    }
+
+    if (src.fImage == NULL) {
+        return true;
+    }
+
+    // create a larger buffer for the other two channels (should force fBlur to do this for us)
+
+    {
+        uint8_t* alphaPlane = dst->fImage;
+        size_t   planeSize = dst->computeImageSize();
+        if (0 == planeSize) {
+            return false;   // too big to allocate, abort
+        }
+        dst->fImage = SkMask::AllocImage(planeSize * 3);
+        memcpy(dst->fImage, alphaPlane, planeSize);
+        SkMask::FreeImage(alphaPlane);
+    }
+
+    // run the light direction through the matrix...
+    Light   light = fLight;
+    matrix.mapVectors((SkVector*)(void*)light.fDirection,
+                      (SkVector*)(void*)fLight.fDirection, 1);
+
+    // now restore the length of the XY component
+    // cast to SkVector so we can call setLength (this double cast silences alias warnings)
+    SkVector* vec = (SkVector*)(void*)light.fDirection;
+    vec->setLength(light.fDirection[0],
+                   light.fDirection[1],
+                   SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1]));
+
+    SkEmbossMask::Emboss(dst, light);
+
+    // restore original alpha
+    memcpy(dst->fImage, src.fImage, src.computeImageSize());
+
+    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(fLight.fPad == 0); // for the font-cache lookup to be clean
+    fBlurRadius = buffer.readScalar();
+}
+
+void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+
+    fLight.fPad = 0;    // for the font-cache lookup to be clean
+    buffer.writeMul4(&fLight, sizeof(fLight));
+    buffer.writeScalar(fBlurRadius);
+}
+
diff --git a/legacy/src/effects/SkEmbossMask_Table.h b/legacy/src/effects/SkEmbossMask_Table.h
new file mode 100644
index 0000000..fc29a15
--- /dev/null
+++ b/legacy/src/effects/SkEmbossMask_Table.h
@@ -0,0 +1,1038 @@
+
+/*
+ * 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 "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, 
+    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/legacy/src/effects/SkGradientShader.cpp
similarity index 100%
rename from src/effects/SkGradientShader.cpp
rename to legacy/src/effects/SkGradientShader.cpp
diff --git a/src/effects/SkGroupShape.cpp b/legacy/src/effects/SkGroupShape.cpp
similarity index 100%
rename from src/effects/SkGroupShape.cpp
rename to legacy/src/effects/SkGroupShape.cpp
diff --git a/legacy/src/effects/SkKernel33MaskFilter.cpp b/legacy/src/effects/SkKernel33MaskFilter.cpp
new file mode 100644
index 0000000..852168c
--- /dev/null
+++ b/legacy/src/effects/SkKernel33MaskFilter.cpp
@@ -0,0 +1,126 @@
+
+/*
+ * 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 "SkKernel33MaskFilter.h"
+#include "SkColorPriv.h"
+
+SkMask::Format SkKernel33ProcMaskFilter::getFormat() {
+    return SkMask::kA8_Format;
+}
+
+bool SkKernel33ProcMaskFilter::filterMask(SkMask* dst, const SkMask& src,
+                                          const SkMatrix&, SkIPoint* margin) {
+    // 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;
+    const uint8_t* srcImage = src.fImage;
+    uint8_t* dstImage = dst->fImage;
+
+    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++) {
+            memset(storage, 0, sizeof(storage));
+            uint8_t* storagePtr = &storage[0][0];
+
+            for (int ky = y - 1; ky <= y + 1; ky++) {
+                const uint8_t* srcRow = srcImage + ky * srcRB;  // may be out-of-range
+                for (int kx = x - 1; kx <= x + 1; kx++) {
+                    if ((unsigned)ky < (unsigned)h && (unsigned)kx < (unsigned)w) {
+                        *storagePtr = srcRow[kx];
+                    }
+                    storagePtr++;
+                }
+            }            
+            int value = this->computeValue(srcRows);
+            
+            if (scale < 256) {
+                value = SkAlphaBlend(value, srcRows[1][1], scale);
+            }
+            *dstRow++ = SkToU8(value);
+        }
+        dstImage += dst->fRowBytes;
+    }
+    return true;
+}
+
+void SkKernel33ProcMaskFilter::flatten(SkFlattenableWriteBuffer& wb) {
+    this->INHERITED::flatten(wb);
+    wb.write32(fPercent256);
+}
+
+SkKernel33ProcMaskFilter::SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb)
+        : SkMaskFilter(rb) {
+    fPercent256 = rb.readS32();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint8_t SkKernel33MaskFilter::computeValue(uint8_t* const* srcRows) {
+    int value = 0;
+
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            value += fKernel[i][j] * srcRows[i][j];
+        }
+    }
+    
+    value >>= fShift;
+
+    if (value < 0) {
+        value = 0;
+    } else if (value > 255) {
+        value = 255;
+    }
+    return (uint8_t)value;
+}
+
+void SkKernel33MaskFilter::flatten(SkFlattenableWriteBuffer& wb) {
+    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);
+}
+
+SkKernel33MaskFilter::SkKernel33MaskFilter(SkFlattenableReadBuffer& rb)
+        : SkKernel33ProcMaskFilter(rb) {
+    rb.read(fKernel, 9 * sizeof(int));
+    fShift = rb.readS32();
+}
+
diff --git a/legacy/src/effects/SkLayerDrawLooper.cpp b/legacy/src/effects/SkLayerDrawLooper.cpp
new file mode 100644
index 0000000..16636dc
--- /dev/null
+++ b/legacy/src/effects/SkLayerDrawLooper.cpp
@@ -0,0 +1,250 @@
+
+/*
+ * 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 "SkColor.h"
+#include "SkLayerDrawLooper.h"
+#include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+SkLayerDrawLooper::LayerInfo::LayerInfo() {
+    fFlagsMask = 0;                     // ignore our paint flags
+    fPaintBits = 0;                     // ignore our paint fields
+    fColorMode = SkXfermode::kDst_Mode; // ignore our color
+    fOffset.set(0, 0);
+    fPostTranslate = false;
+}
+
+SkLayerDrawLooper::SkLayerDrawLooper()
+        : fRecs(NULL),
+          fCount(0),
+          fCurrRec(NULL) {
+}
+
+SkLayerDrawLooper::~SkLayerDrawLooper() {
+    Rec* rec = fRecs;
+    while (rec) {
+        Rec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+}
+
+SkPaint* SkLayerDrawLooper::addLayer(const LayerInfo& info) {
+    fCount += 1;
+
+    Rec* rec = SkNEW(Rec);
+    rec->fNext = fRecs;
+    rec->fInfo = info;
+    fRecs = rec;
+
+    return &rec->fPaint;
+}
+
+void SkLayerDrawLooper::addLayer(SkScalar dx, SkScalar dy) {
+    LayerInfo info;
+
+    info.fOffset.set(dx, dy);
+    (void)this->addLayer(info);
+}
+
+void SkLayerDrawLooper::init(SkCanvas* canvas) {
+    fCurrRec = fRecs;
+    canvas->save(SkCanvas::kMatrix_SaveFlag);
+}
+
+static SkColor xferColor(SkColor src, SkColor dst, SkXfermode::Mode mode) {
+    switch (mode) {
+        case SkXfermode::kSrc_Mode:
+            return src;
+        case SkXfermode::kDst_Mode:
+            return dst;
+        default: {
+            SkPMColor pmS = SkPreMultiplyColor(src);
+            SkPMColor pmD = SkPreMultiplyColor(dst);
+            SkPMColor result = SkXfermode::GetProc(mode)(pmS, pmD);
+            return SkUnPreMultiply::PMColorToColor(result);
+        }
+    }
+}
+
+// Even with kEntirePaint_Bits, we always ensure that the master paint's
+// text-encoding is respected, since that controls how we interpret the
+// text/length parameters of a draw[Pos]Text call.
+void SkLayerDrawLooper::ApplyInfo(SkPaint* dst, const SkPaint& src,
+                                  const LayerInfo& info) {
+
+    uint32_t mask = info.fFlagsMask;
+    dst->setFlags((dst->getFlags() & ~mask) | (src.getFlags() & mask));
+    dst->setColor(xferColor(src.getColor(), dst->getColor(), info.fColorMode));
+
+    BitFlags bits = info.fPaintBits;
+    SkPaint::TextEncoding encoding = dst->getTextEncoding();
+
+    if (0 == bits) {
+        return;
+    }
+    if (kEntirePaint_Bits == bits) {
+        // we've already computed these, so save it from the assignment
+        uint32_t f = dst->getFlags();
+        SkColor c = dst->getColor();
+        *dst = src;
+        dst->setFlags(f);
+        dst->setColor(c);
+        dst->setTextEncoding(encoding);
+        return;
+    }
+
+    if (bits & kStyle_Bit) {
+        dst->setStyle(src.getStyle());
+        dst->setStrokeWidth(src.getStrokeWidth());
+        dst->setStrokeMiter(src.getStrokeMiter());
+        dst->setStrokeCap(src.getStrokeCap());
+        dst->setStrokeJoin(src.getStrokeJoin());
+    }
+
+    if (bits & kTextSkewX_Bit) {
+        dst->setTextSkewX(src.getTextSkewX());
+    }
+
+    if (bits & kPathEffect_Bit) {
+        dst->setPathEffect(src.getPathEffect());
+    }
+    if (bits & kMaskFilter_Bit) {
+        dst->setMaskFilter(src.getMaskFilter());
+    }
+    if (bits & kShader_Bit) {
+        dst->setShader(src.getShader());
+    }
+    if (bits & kColorFilter_Bit) {
+        dst->setColorFilter(src.getColorFilter());
+    }
+    if (bits & kXfermode_Bit) {
+        dst->setXfermode(src.getXfermode());
+    }
+
+    // we don't override these
+#if 0
+    dst->setTypeface(src.getTypeface());
+    dst->setTextSize(src.getTextSize());
+    dst->setTextScaleX(src.getTextScaleX());
+    dst->setRasterizer(src.getRasterizer());
+    dst->setLooper(src.getLooper());
+    dst->setTextEncoding(src.getTextEncoding());
+    dst->setHinting(src.getHinting());
+#endif
+}
+
+// Should we add this to canvas?
+static void postTranslate(SkCanvas* canvas, SkScalar dx, SkScalar dy) {
+    SkMatrix m = canvas->getTotalMatrix();
+    m.postTranslate(dx, dy);
+    canvas->setMatrix(m);
+}
+
+bool SkLayerDrawLooper::next(SkCanvas* canvas, SkPaint* paint) {
+    canvas->restore();
+    if (NULL == fCurrRec) {
+        return false;
+    }
+
+    ApplyInfo(paint, fCurrRec->fPaint, fCurrRec->fInfo);
+
+    canvas->save(SkCanvas::kMatrix_SaveFlag);
+    if (fCurrRec->fInfo.fPostTranslate) {
+        postTranslate(canvas, fCurrRec->fInfo.fOffset.fX,
+                      fCurrRec->fInfo.fOffset.fY);
+    } else {
+        canvas->translate(fCurrRec->fInfo.fOffset.fX, fCurrRec->fInfo.fOffset.fY);
+    }
+    fCurrRec = fCurrRec->fNext;
+
+    return true;
+}
+
+SkLayerDrawLooper::Rec* SkLayerDrawLooper::Rec::Reverse(Rec* head) {
+    Rec* rec = head;
+    Rec* prev = NULL;
+    while (rec) {
+        Rec* next = rec->fNext;
+        rec->fNext = prev;
+        prev = rec;
+        rec = next;
+    }
+    return prev;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkLayerDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+
+#ifdef SK_DEBUG
+    {
+        Rec* rec = fRecs;
+        int count = 0;
+        while (rec) {
+            rec = rec->fNext;
+            count += 1;
+        }
+        SkASSERT(count == fCount);
+    }
+#endif
+
+    buffer.writeInt(fCount);
+    
+    Rec* rec = fRecs;
+    for (int i = 0; i < fCount; i++) {
+        buffer.writeInt(rec->fInfo.fPaintBits);
+        buffer.writeInt(rec->fInfo.fColorMode);
+        buffer.writeScalar(rec->fInfo.fOffset.fX);
+        buffer.writeScalar(rec->fInfo.fOffset.fY);
+        buffer.writeBool(rec->fInfo.fPostTranslate);
+        rec->fPaint.flatten(buffer);
+        rec = rec->fNext;
+    }
+}
+
+SkLayerDrawLooper::SkLayerDrawLooper(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer),
+          fRecs(NULL),
+          fCount(0),
+          fCurrRec(NULL) {
+    int count = buffer.readInt();
+
+    for (int i = 0; i < count; i++) {
+        LayerInfo info;
+        if (buffer.getPictureVersion() == PICTURE_VERSION_ICS)
+            info.fFlagsMask = buffer.readInt();
+        info.fPaintBits = buffer.readInt();
+        info.fColorMode = (SkXfermode::Mode)buffer.readInt();
+        info.fOffset.fX = buffer.readScalar();
+        info.fOffset.fY = buffer.readScalar();
+        info.fPostTranslate = buffer.readBool();
+        this->addLayer(info)->unflatten(buffer);
+    }
+    SkASSERT(count == fCount);
+
+    // we're in reverse order, so fix it now
+    fRecs = Rec::Reverse(fRecs);
+    
+#ifdef SK_DEBUG
+    {
+        Rec* rec = fRecs;
+        int n = 0;
+        while (rec) {
+            rec = rec->fNext;
+            n += 1;
+        }
+        SkASSERT(count == n);
+    }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkLayerDrawLooper)
diff --git a/legacy/src/effects/SkLayerRasterizer.cpp b/legacy/src/effects/SkLayerRasterizer.cpp
new file mode 100644
index 0000000..9b29550
--- /dev/null
+++ b/legacy/src/effects/SkLayerRasterizer.cpp
@@ -0,0 +1,228 @@
+
+/*
+ * 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 "SkLayerRasterizer.h"
+#include "SkBuffer.h"
+#include "SkDraw.h"
+#include "SkMask.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "../core/SkRasterClip.h"
+#include "SkXfermode.h"
+#include <new>
+
+struct SkLayerRasterizer_Rec {
+    SkPaint     fPaint;
+    SkVector    fOffset;
+};
+
+SkLayerRasterizer::SkLayerRasterizer() : fLayers(sizeof(SkLayerRasterizer_Rec))
+{
+}
+
+SkLayerRasterizer::~SkLayerRasterizer() {
+    SkDeque::F2BIter        iter(fLayers);
+    SkLayerRasterizer_Rec*  rec;
+
+    while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL)
+        rec->fPaint.~SkPaint();
+}
+
+void SkLayerRasterizer::addLayer(const SkPaint& paint, SkScalar dx,
+                                 SkScalar dy) {
+    SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back();
+
+    new (&rec->fPaint) SkPaint(paint);
+    rec->fOffset.set(dx, dy);
+}
+
+static bool compute_bounds(const SkDeque& layers, const SkPath& path,
+                           const SkMatrix& matrix,
+                           const SkIRect* clipBounds, SkIRect* bounds) {
+    SkDeque::F2BIter        iter(layers);
+    SkLayerRasterizer_Rec*  rec;
+
+    bounds->set(SK_MaxS32, SK_MaxS32, SK_MinS32, SK_MinS32);
+
+    while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) {
+        const SkPaint&  paint = rec->fPaint;
+        SkPath          fillPath, devPath;
+        const SkPath*   p = &path;
+
+        if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
+            paint.getFillPath(path, &fillPath);
+            p = &fillPath;
+        }
+        if (p->isEmpty()) {
+            continue;
+        }
+
+        // apply the matrix and offset
+        {
+            SkMatrix m = matrix;
+            m.preTranslate(rec->fOffset.fX, rec->fOffset.fY);
+            p->transform(m, &devPath);
+        }
+
+        SkMask  mask;
+        if (!SkDraw::DrawToMask(devPath, clipBounds, paint.getMaskFilter(),
+                                &matrix, &mask,
+                                SkMask::kJustComputeBounds_CreateMode)) {
+            return false;
+        }
+
+        bounds->join(mask.fBounds);
+    }
+    return true;
+}
+
+bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix,
+                                    const SkIRect* clipBounds,
+                                    SkMask* mask, SkMask::CreateMode mode) {
+    if (fLayers.empty()) {
+        return false;
+    }
+
+    if (SkMask::kJustRenderImage_CreateMode != mode) {
+        if (!compute_bounds(fLayers, path, matrix, clipBounds, &mask->fBounds))
+            return false;
+    }
+
+    if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
+        mask->fFormat   = SkMask::kA8_Format;
+        mask->fRowBytes = mask->fBounds.width();
+        size_t size = mask->computeImageSize();
+        if (0 == size) {
+            return false;   // too big to allocate, abort
+        }
+        mask->fImage = SkMask::AllocImage(size);
+        memset(mask->fImage, 0, size);
+    }
+
+    if (SkMask::kJustComputeBounds_CreateMode != mode) {
+        SkBitmap        device;
+        SkRasterClip    rectClip;
+        SkDraw          draw;
+        SkMatrix        translatedMatrix;  // this translates us to our local pixels
+        SkMatrix        drawMatrix;        // this translates the path by each layer's offset
+
+        rectClip.setRect(SkIRect::MakeWH(mask->fBounds.width(), mask->fBounds.height()));
+
+        translatedMatrix = matrix;
+        translatedMatrix.postTranslate(-SkIntToScalar(mask->fBounds.fLeft),
+                                       -SkIntToScalar(mask->fBounds.fTop));
+
+        device.setConfig(SkBitmap::kA8_Config, mask->fBounds.width(), mask->fBounds.height(), mask->fRowBytes);
+        device.setPixels(mask->fImage);
+
+        draw.fBitmap    = &device;
+        draw.fMatrix    = &drawMatrix;
+        draw.fRC        = &rectClip;
+        draw.fClip      = &rectClip.bwRgn();
+        // we set the matrixproc in the loop, as the matrix changes each time (potentially)
+        draw.fBounder   = NULL;
+
+        SkDeque::F2BIter        iter(fLayers);
+        SkLayerRasterizer_Rec*  rec;
+
+        while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) {
+            drawMatrix = translatedMatrix;
+            drawMatrix.preTranslate(rec->fOffset.fX, rec->fOffset.fY);
+            draw.drawPath(path, rec->fPaint);
+        }
+    }
+    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();
+
+    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();
+    }
+}
+
+void SkLayerRasterizer::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+
+    buffer.write32(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);
+    }
+}
+
+SkFlattenable* SkLayerRasterizer::CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkLayerRasterizer, (buffer));
+}
+
+SkFlattenable::Factory SkLayerRasterizer::getFactory() {
+    return CreateProc;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkLayerRasterizer)
+
diff --git a/legacy/src/effects/SkMorphologyImageFilter.cpp b/legacy/src/effects/SkMorphologyImageFilter.cpp
new file mode 100644
index 0000000..78fabc5
--- /dev/null
+++ b/legacy/src/effects/SkMorphologyImageFilter.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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 "SkMorphologyImageFilter.h"
+#include "SkColorPriv.h"
+
+SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    fRadius.fWidth = buffer.readScalar();
+    fRadius.fHeight = buffer.readScalar();
+}
+
+SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY)
+    : fRadius(SkISize::Make(radiusX, radiusY)) {
+}
+
+
+void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fRadius.fWidth);
+    buffer.writeScalar(fRadius.fHeight);
+}
+
+static void erode(const SkPMColor* src, SkPMColor* dst,
+                  int radius, int width, int height,
+                  int srcStrideX, int srcStrideY,
+                  int dstStrideX, int dstStrideY)
+{
+    const SkPMColor* upperSrc = src + SkMin32(radius, width - 1) * srcStrideX;
+    for (int x = 0; x < width; ++x) {
+        const SkPMColor* lp = src;
+        const SkPMColor* up = upperSrc;
+        SkPMColor* dptr = dst;
+        for (int y = 0; y < height; ++y) {
+            int minB = 255, minG = 255, minR = 255, minA = 255;
+            for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
+                int b = SkGetPackedB32(*p);
+                int g = SkGetPackedG32(*p);
+                int r = SkGetPackedR32(*p);
+                int a = SkGetPackedA32(*p);
+                if (b < minB) minB = b;
+                if (g < minG) minG = g;
+                if (r < minR) minR = r;
+                if (a < minA) minA = a;
+            }
+            *dptr = SkPackARGB32(minA, minR, minG, minB);
+            dptr += dstStrideY;
+            lp += srcStrideY;
+            up += srcStrideY;
+        }
+        if (x >= radius) src += srcStrideX;
+        if (x + radius < width - 1) upperSrc += srcStrideX;
+        dst += dstStrideX;
+    }
+}
+
+static void erodeX(const SkBitmap& src, SkBitmap* dst, int radiusX)
+{
+    erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+          radiusX, src.width(), src.height(),
+          1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
+}
+
+static void erodeY(const SkBitmap& src, SkBitmap* dst, int radiusY)
+{
+    erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+          radiusY, src.height(), src.width(),
+          src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
+}
+
+static void dilate(const SkPMColor* src, SkPMColor* dst,
+                   int radius, int width, int height,
+                   int srcStrideX, int srcStrideY,
+                   int dstStrideX, int dstStrideY)
+{
+    const SkPMColor* upperSrc = src + SkMin32(radius, width - 1) * srcStrideX;
+    for (int x = 0; x < width; ++x) {
+        const SkPMColor* lp = src;
+        const SkPMColor* up = upperSrc;
+        SkPMColor* dptr = dst;
+        for (int y = 0; y < height; ++y) {
+            int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
+            for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
+                int b = SkGetPackedB32(*p);
+                int g = SkGetPackedG32(*p);
+                int r = SkGetPackedR32(*p);
+                int a = SkGetPackedA32(*p);
+                if (b > maxB) maxB = b;
+                if (g > maxG) maxG = g;
+                if (r > maxR) maxR = r;
+                if (a > maxA) maxA = a;
+            }
+            *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
+            dptr += dstStrideY;
+            lp += srcStrideY;
+            up += srcStrideY;
+        }
+        if (x >= radius) src += srcStrideX;
+        if (x + radius < width - 1) upperSrc += srcStrideX;
+        dst += dstStrideX;
+    }
+}
+
+static void dilateX(const SkBitmap& src, SkBitmap* dst, int radiusX)
+{
+    dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+           radiusX, src.width(), src.height(),
+           1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
+}
+
+static void dilateY(const SkBitmap& src, SkBitmap* dst, int radiusY)
+{
+    dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
+           radiusY, src.height(), src.width(),
+           src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
+}
+
+bool SkErodeImageFilter::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;
+    }
+
+    dst->setConfig(src.config(), src.width(), src.height());
+    dst->allocPixels();
+
+    int width = radius().width();
+    int height = radius().height();
+
+    if (width < 0 || height < 0) {
+        return false;
+    }
+
+    if (width == 0 && height == 0) {
+        src.copyTo(dst, dst->config());
+        return true;
+    }
+
+    SkBitmap temp;
+    temp.setConfig(dst->config(), dst->width(), dst->height());
+    if (!temp.allocPixels()) {
+        return false;
+    }
+
+    if (width > 0 && height > 0) {
+        erodeX(src, &temp, width);
+        erodeY(temp, dst, height);
+    } else if (width > 0) {
+        erodeX(src, dst, width);
+    } else if (height > 0) {
+        erodeY(src, dst, height);
+    }
+    return true;
+}
+
+bool SkDilateImageFilter::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;
+    }
+
+    dst->setConfig(src.config(), src.width(), src.height());
+    dst->allocPixels();
+
+    int width = radius().width();
+    int height = radius().height();
+
+    if (width < 0 || height < 0) {
+        return false;
+    }
+
+    if (width == 0 && height == 0) {
+        src.copyTo(dst, dst->config());
+        return true;
+    }
+
+    SkBitmap temp;
+    temp.setConfig(dst->config(), dst->width(), dst->height());
+    if (!temp.allocPixels()) {
+        return false;
+    }
+
+    if (width > 0 && height > 0) {
+        dilateX(src, &temp, width);
+        dilateY(temp, dst, height);
+    } else if (width > 0) {
+        dilateX(src, dst, width);
+    } else if (height > 0) {
+        dilateY(src, dst, height);
+    }
+    return true;
+}
+
+bool SkDilateImageFilter::asADilate(SkISize* radius) const {
+    *radius = this->radius();
+    return true;
+}
+
+bool SkErodeImageFilter::asAnErode(SkISize* radius) const {
+    *radius = this->radius();
+    return true;
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkDilateImageFilter)
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkErodeImageFilter)
diff --git a/legacy/src/effects/SkPaintFlagsDrawFilter.cpp b/legacy/src/effects/SkPaintFlagsDrawFilter.cpp
new file mode 100644
index 0000000..1fe4402
--- /dev/null
+++ b/legacy/src/effects/SkPaintFlagsDrawFilter.cpp
@@ -0,0 +1,22 @@
+
+/*
+ * 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 "SkPaintFlagsDrawFilter.h"
+#include "SkPaint.h"
+
+SkPaintFlagsDrawFilter::SkPaintFlagsDrawFilter(uint32_t clearFlags,
+                                               uint32_t setFlags) {
+    fClearFlags = SkToU16(clearFlags & SkPaint::kAllFlags);
+    fSetFlags = SkToU16(setFlags & SkPaint::kAllFlags);
+}
+
+void SkPaintFlagsDrawFilter::filter(SkPaint* paint, Type) {
+    paint->setFlags((paint->getFlags() & ~fClearFlags) | fSetFlags);
+}
+
diff --git a/legacy/src/effects/SkPixelXorXfermode.cpp b/legacy/src/effects/SkPixelXorXfermode.cpp
new file mode 100644
index 0000000..935a475
--- /dev/null
+++ b/legacy/src/effects/SkPixelXorXfermode.cpp
@@ -0,0 +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 "SkPixelXorXfermode.h"
+#include "SkColorPriv.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 res = src ^ dst ^ fOpColor;
+    res |= (SK_A32_MASK << SK_A32_SHIFT);   // force it to be opaque
+    return res;
+}
+
+void SkPixelXorXfermode::flatten(SkFlattenableWriteBuffer& wb) {
+    this->INHERITED::flatten(wb);
+    wb.write32(fOpColor);
+}
+
+SkPixelXorXfermode::SkPixelXorXfermode(SkFlattenableReadBuffer& rb)
+        : SkXfermode(rb) {
+    fOpColor = rb.readU32();
+}
+
+SkFlattenable::Factory SkPixelXorXfermode::getFactory() {
+    return Create;
+}
+
+SkFlattenable* SkPixelXorXfermode::Create(SkFlattenableReadBuffer& rb) {
+    return SkNEW_ARGS(SkPixelXorXfermode, (rb));
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkPixelXorXfermode)
diff --git a/legacy/src/effects/SkPorterDuff.cpp b/legacy/src/effects/SkPorterDuff.cpp
new file mode 100644
index 0000000..8acb345
--- /dev/null
+++ b/legacy/src/effects/SkPorterDuff.cpp
@@ -0,0 +1,88 @@
+
+/*
+ * 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 "SkPorterDuff.h"
+#include "SkXfermode.h"
+
+/*  This file just exists as a compatibility layer, gluing the PorterDuff API
+    into the (extended) SkXfermode API
+ */
+
+#define MAKE_PAIR(mode) { SkPorterDuff::k##mode##_Mode, SkXfermode::k##mode##_Mode }
+
+// this table must be in SkPorterDuff::Mode order, so it can be indexed directly
+// with a porterduff mode.
+static const struct Pair {
+    SkPorterDuff::Mode  fPD;
+    SkXfermode::Mode    fXF;
+} gPairs[] = {
+    MAKE_PAIR(Clear),
+    MAKE_PAIR(Src),
+    MAKE_PAIR(Dst),
+    MAKE_PAIR(SrcOver),
+    MAKE_PAIR(DstOver),
+    MAKE_PAIR(SrcIn),
+    MAKE_PAIR(DstIn),
+    MAKE_PAIR(SrcOut),
+    MAKE_PAIR(DstOut),
+    MAKE_PAIR(SrcATop),
+    MAKE_PAIR(DstATop),
+    MAKE_PAIR(Xor),
+    MAKE_PAIR(Darken),
+    MAKE_PAIR(Lighten),
+    MAKE_PAIR(Multiply),
+    MAKE_PAIR(Screen),
+    { SkPorterDuff::kAdd_Mode, SkXfermode::kPlus_Mode },
+#ifdef SK_BUILD_FOR_ANDROID
+    MAKE_PAIR(Overlay),
+#endif
+};
+
+static bool find_pdmode(SkXfermode::Mode src, SkPorterDuff::Mode* dst) {
+    const Pair* pairs = gPairs;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        if (pairs[i].fXF == src) {
+            if (dst) {
+                *dst = pairs[i].fPD;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+SkXfermode::Mode SkPorterDuff::ToXfermodeMode(Mode mode) {
+    SkASSERT((unsigned)mode < SkPorterDuff::kModeCount);
+    const Pair& pair = gPairs[mode];
+    SkASSERT(pair.fPD == mode);
+    return pair.fXF;
+}
+
+SkXfermode* SkPorterDuff::CreateXfermode(SkPorterDuff::Mode mode) {
+    const Pair& pair = gPairs[mode];
+    SkASSERT(pair.fPD == mode);
+    return SkXfermode::Create(pair.fXF);
+}
+
+bool SkPorterDuff::IsMode(SkXfermode* xfer, Mode* pdmode) {
+    SkXfermode::Mode xfmode;
+    if (!SkXfermode::IsMode(xfer, &xfmode)) {
+        return false;
+    }
+    return find_pdmode(xfmode, pdmode);
+}
+
+SkXfermodeProc SkPorterDuff::GetXfermodeProc(Mode mode) {
+    return SkXfermode::GetProc(gPairs[mode].fXF);
+}
+
+SkXfermodeProc16 SkPorterDuff::GetXfermodeProc16(Mode mode, SkColor srcColor) {
+    return SkXfermode::GetProc16(gPairs[mode].fXF, srcColor);
+}
+
diff --git a/src/effects/SkRadialGradient_Table.h b/legacy/src/effects/SkRadialGradient_Table.h
similarity index 100%
rename from src/effects/SkRadialGradient_Table.h
rename to legacy/src/effects/SkRadialGradient_Table.h
diff --git a/src/effects/SkRectShape.cpp b/legacy/src/effects/SkRectShape.cpp
similarity index 100%
rename from src/effects/SkRectShape.cpp
rename to legacy/src/effects/SkRectShape.cpp
diff --git a/legacy/src/effects/SkTableColorFilter.cpp b/legacy/src/effects/SkTableColorFilter.cpp
new file mode 100644
index 0000000..800d4a9
--- /dev/null
+++ b/legacy/src/effects/SkTableColorFilter.cpp
@@ -0,0 +1,220 @@
+#include "SkColorPriv.h"
+#include "SkTableColorFilter.h"
+#include "SkUnPreMultiply.h"
+
+class SkTable_ColorFilter : public SkColorFilter {
+public:
+    SkTable_ColorFilter(const uint8_t tableA[], const uint8_t tableR[],
+                        const uint8_t tableG[], const uint8_t tableB[]) {
+        fBitmap = NULL;
+        fFlags = 0;
+
+        uint8_t* dst = fStorage;
+        if (tableA) {
+            memcpy(dst, tableA, 256);
+            dst += 256;
+            fFlags |= kA_Flag;
+        }
+        if (tableR) {
+            memcpy(dst, tableR, 256);
+            dst += 256;
+            fFlags |= kR_Flag;
+        }
+        if (tableG) {
+            memcpy(dst, tableG, 256);
+            dst += 256;
+            fFlags |= kG_Flag;
+        }
+        if (tableB) {
+            memcpy(dst, tableB, 256);
+            fFlags |= kB_Flag;
+        }
+    }
+
+    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));
+    }
+
+protected:
+    SkTable_ColorFilter(SkFlattenableReadBuffer& buffer);
+
+private:
+    SkBitmap* fBitmap;
+
+    enum {
+        kA_Flag = 1 << 0,
+        kR_Flag = 1 << 1,
+        kG_Flag = 1 << 2,
+        kB_Flag = 1 << 3,
+    };
+    uint8_t fStorage[256 * 4];
+    unsigned fFlags;
+
+    typedef SkColorFilter INHERITED;
+};
+
+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, 
+    0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
+};
+
+void SkTable_ColorFilter::filterSpan(const SkPMColor src[], int count,
+                                     SkPMColor dst[]) {
+    const uint8_t* table = fStorage;
+    const uint8_t* tableA = gIdentityTable;
+    const uint8_t* tableR = gIdentityTable;
+    const uint8_t* tableG = gIdentityTable;
+    const uint8_t* tableB = gIdentityTable;
+    if (fFlags & kA_Flag) {
+        tableA = table; table += 256;
+    }
+    if (fFlags & kR_Flag) {
+        tableR = table; table += 256;
+    }
+    if (fFlags & kG_Flag) {
+        tableG = table; table += 256;
+    }
+    if (fFlags & kB_Flag) {
+        tableB = table;
+    }
+
+    const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable();
+    for (int i = 0; i < count; ++i) {
+        SkPMColor c = src[i];
+        unsigned a, r, g, b;
+        if (0 == c) {
+            a = r = g = b = 0;
+        } else {
+            a = SkGetPackedA32(c);
+            r = SkGetPackedR32(c);
+            g = SkGetPackedG32(c);
+            b = SkGetPackedB32(c);
+
+            if (a < 255) {
+                SkUnPreMultiply::Scale scale = scaleTable[a];
+                r = SkUnPreMultiply::ApplyScale(scale, r);
+                g = SkUnPreMultiply::ApplyScale(scale, g);
+                b = SkUnPreMultiply::ApplyScale(scale, b);
+            }
+        }
+        dst[i] = SkPremultiplyARGBInline(tableA[a], tableR[r],
+                                         tableG[g], tableB[b]);
+    }
+}
+
+SkFlattenable::Factory SkTable_ColorFilter::getFactory() {
+    return CreateProc;
+}
+
+static const uint8_t gCountNibBits[] = {
+    0, 1, 1, 2,
+    1, 2, 2, 3,
+    1, 2, 2, 3,
+    2, 3, 3, 4
+};
+
+#include "SkPackBits.h"
+
+void SkTable_ColorFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+
+    uint8_t storage[5*256];
+    int count = gCountNibBits[fFlags & 0xF];
+    size_t size = SkPackBits::Pack8(fStorage, count * 256, storage);
+    SkASSERT(size <= sizeof(storage));
+
+//    SkDebugf("raw %d packed %d\n", count * 256, size);
+    
+    buffer.writeInt(fFlags);
+    buffer.writeInt(size);
+    buffer.write(storage, size);
+}
+
+SkTable_ColorFilter::SkTable_ColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fBitmap = NULL;
+
+    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);
+
+    SkASSERT(raw <= sizeof(fStorage));
+    size_t count = gCountNibBits[fFlags & 0xF];
+    SkASSERT(raw == count * 256);
+}
+
+bool SkTable_ColorFilter::asComponentTable(SkBitmap* table) {
+    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);
+        }
+        *table = *fBitmap;
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_CPU_BENDIAN
+#else
+    #define SK_A32_INDEX    (3 - (SK_A32_SHIFT >> 3))
+    #define SK_R32_INDEX    (3 - (SK_R32_SHIFT >> 3))
+    #define SK_G32_INDEX    (3 - (SK_G32_SHIFT >> 3))
+    #define SK_B32_INDEX    (3 - (SK_B32_SHIFT >> 3))
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkColorFilter* SkTableColorFilter::Create(const uint8_t table[256]) {
+    return SkNEW_ARGS(SkTable_ColorFilter, (table, table, table, table));
+}
+
+SkColorFilter* SkTableColorFilter::CreateARGB(const uint8_t tableA[256],
+                                              const uint8_t tableR[256],
+                                              const uint8_t tableG[256],
+                                              const uint8_t tableB[256]) {
+    return SkNEW_ARGS(SkTable_ColorFilter, (tableA, tableR, tableG, tableB));
+}
diff --git a/legacy/src/effects/SkTableMaskFilter.cpp b/legacy/src/effects/SkTableMaskFilter.cpp
new file mode 100644
index 0000000..4024372
--- /dev/null
+++ b/legacy/src/effects/SkTableMaskFilter.cpp
@@ -0,0 +1,139 @@
+
+/*
+ * 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 "SkTableMaskFilter.h"
+
+SkTableMaskFilter::SkTableMaskFilter() {
+    for (int i = 0; i < 256; i++) {
+        fTable[i] = i;
+    }
+}
+
+SkTableMaskFilter::SkTableMaskFilter(const uint8_t table[256]) {
+    this->setTable(table);
+}
+
+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) {
+    if (src.fFormat != SkMask::kA8_Format) {
+        return false;
+    }
+
+    dst->fBounds = src.fBounds;
+    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]];
+            }
+            srcP += src.fRowBytes;
+            // we can't just inc dstP by rowbytes, because if it has any
+            // padding between its width and its rowbytes, we need to zero those
+            // so that the bitters can read those safely if that is faster for
+            // them
+            dstP += dstWidth;
+            for (int i = extraZeros - 1; i >= 0; --i) {
+                *dstP++ = 0;
+            }
+        }
+    }
+
+    if (margin) {
+        margin->set(0, 0);
+    }
+    return true;
+}
+
+SkMask::Format SkTableMaskFilter::getFormat() {
+    return SkMask::kA8_Format;
+}
+
+void SkTableMaskFilter::flatten(SkFlattenableWriteBuffer& wb) {
+    this->INHERITED::flatten(wb);
+    wb.writePad(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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTableMaskFilter::MakeGammaTable(uint8_t table[256], SkScalar gamma) {
+    const float dx = 1 / 255.0f;
+    const float g = SkScalarToFloat(gamma);
+
+    float x = 0;
+    for (int i = 0; i < 256; i++) {
+        float ee = powf(x, g) * 255;
+        table[i] = SkPin32(sk_float_round2int(powf(x, g) * 255), 0, 255);
+        x += dx;
+    }
+}
+
+void SkTableMaskFilter::MakeClipTable(uint8_t table[256], uint8_t min,
+                                      uint8_t max) {
+    if (0 == max) {
+        max = 1;
+    }
+    if (min >= max) {
+        min = max - 1;
+    }
+    SkASSERT(min < max);
+
+    SkFixed scale = (1 << 16) * 255 / (max - min);
+    memset(table, 0, min + 1);
+    for (int i = min + 1; i < max; i++) {
+        int value = SkFixedRound(scale * (i - min));
+        SkASSERT(value <= 255);
+        table[i] = value;
+    }
+    memset(table + max, 255, 256 - max);
+
+#if 0
+    int j;
+    for (j = 0; j < 256; j++) {
+        if (table[j]) {
+            break;
+        }
+    }
+    SkDebugf("%d %d start [%d]", min, max, j);
+    for (; j < 256; j++) {
+        SkDebugf(" %d", table[j]);
+    }
+    SkDebugf("\n\n");
+#endif
+}
+
diff --git a/legacy/src/effects/SkTestImageFilters.cpp b/legacy/src/effects/SkTestImageFilters.cpp
new file mode 100755
index 0000000..a772f64
--- /dev/null
+++ b/legacy/src/effects/SkTestImageFilters.cpp
@@ -0,0 +1,401 @@
+#include "SkTestImageFilters.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+
+class OwnDeviceCanvas : public SkCanvas {
+public:
+    OwnDeviceCanvas(SkDevice* device) : SkCanvas(device) {
+        SkSafeUnref(device);
+    }
+};
+
+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,
+                                         const SkBitmap& src,
+                                         const SkMatrix& ctm,
+                                         SkBitmap* result,
+                                         SkIPoint* loc) {
+    if (!fOuter && !fInner) {
+        return false;
+    }
+    
+    if (!fOuter || !fInner) {
+        return (fOuter ? fOuter : fInner)->filterImage(proxy, src, ctm, result, loc);
+    }
+    
+    SkBitmap tmp;
+    return fInner->filterImage(proxy, src, ctm, &tmp, loc) &&
+           fOuter->filterImage(proxy, tmp, ctm, result, loc);
+}
+
+bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
+                                          const SkMatrix& ctm,
+                                          SkIRect* dst) {
+    if (!fOuter && !fInner) {
+        return false;
+    }
+    
+    if (!fOuter || !fInner) {
+        return (fOuter ? fOuter : fInner)->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);
+}
+
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkDownSampleImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
+                                            const SkMatrix& matrix,
+                                            SkBitmap* result, SkIPoint*) {
+    SkScalar scale = fScale;
+    if (scale > SK_Scalar1 || scale <= 0) {
+        return false;
+    }
+    
+    int dstW = SkScalarRoundToInt(src.width() * scale);
+    int dstH = SkScalarRoundToInt(src.height() * scale);
+    if (dstW < 1) {
+        dstW = 1;
+    }
+    if (dstH < 1) {
+        dstH = 1;
+    }
+
+    SkBitmap tmp;
+
+    // downsample
+    {
+        SkDevice* dev = proxy->createDevice(dstW, dstH);
+        if (NULL == dev) {
+            return false;
+        }
+        OwnDeviceCanvas canvas(dev);
+        SkPaint paint;
+
+        paint.setFilterBitmap(true);
+        canvas.scale(scale, scale);
+        canvas.drawBitmap(src, 0, 0, &paint);
+        tmp = dev->accessBitmap(false);
+    }
+
+    // upscale
+    {
+        SkDevice* dev = proxy->createDevice(src.width(), src.height());
+        if (NULL == dev) {
+            return false;
+        }
+        OwnDeviceCanvas canvas(dev);
+
+        SkRect r = SkRect::MakeWH(SkIntToScalar(src.width()),
+                                  SkIntToScalar(src.height()));
+        canvas.drawBitmapRect(tmp, NULL, r, NULL);
+        *result = dev->accessBitmap(false);
+    }
+    return true;
+}
+
+void SkDownSampleImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+    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/legacy/src/effects/SkTransparentShader.cpp b/legacy/src/effects/SkTransparentShader.cpp
new file mode 100644
index 0000000..486fc89
--- /dev/null
+++ b/legacy/src/effects/SkTransparentShader.cpp
@@ -0,0 +1,136 @@
+
+/*
+ * 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 "SkTransparentShader.h"
+#include "SkColorPriv.h"
+
+bool SkTransparentShader::setContext(const SkBitmap& device,
+                                     const SkPaint& paint,
+                                     const SkMatrix& matrix) {
+    fDevice = &device;
+    fAlpha = paint.getAlpha();
+
+    return this->INHERITED::setContext(device, paint, matrix);
+}
+
+uint32_t SkTransparentShader::getFlags() {
+    uint32_t flags = this->INHERITED::getFlags();
+
+    switch (fDevice->getConfig()) {
+        case SkBitmap::kRGB_565_Config:
+            flags |= kHasSpan16_Flag;
+            if (fAlpha == 255)
+                flags |= kOpaqueAlpha_Flag;
+            break;
+        case SkBitmap::kARGB_8888_Config:
+        case SkBitmap::kARGB_4444_Config:
+            if (fAlpha == 255 && fDevice->isOpaque())
+                flags |= kOpaqueAlpha_Flag;
+            break;
+        default:
+            break;
+    }
+    return flags;
+}
+
+void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
+    unsigned scale = SkAlpha255To256(fAlpha);
+
+    switch (fDevice->getConfig()) {
+        case SkBitmap::kARGB_8888_Config:
+            if (scale == 256) {
+                SkPMColor* src = fDevice->getAddr32(x, y);
+                if (src != span) {
+                    memcpy(span, src, count * sizeof(SkPMColor));
+                }
+            } else {
+                const SkPMColor* src = fDevice->getAddr32(x, y);
+                for (int i = count - 1; i >= 0; --i) {
+                    span[i] = SkAlphaMulQ(src[i], scale);
+                }
+            }
+            break;
+        case SkBitmap::kRGB_565_Config: {
+            const uint16_t* src = fDevice->getAddr16(x, y);
+            if (scale == 256) {
+                for (int i = count - 1; i >= 0; --i) {
+                    span[i] = SkPixel16ToPixel32(src[i]);
+                }
+            } else {
+                unsigned alpha = fAlpha;
+                for (int i = count - 1; i >= 0; --i) {
+                    uint16_t c = src[i];
+                    unsigned r = SkPacked16ToR32(c);
+                    unsigned g = SkPacked16ToG32(c);
+                    unsigned b = SkPacked16ToB32(c);
+
+                    span[i] = SkPackARGB32( alpha,
+                                            SkAlphaMul(r, scale),
+                                            SkAlphaMul(g, scale),
+                                            SkAlphaMul(b, scale));
+                }
+            }
+            break;
+        }
+        case SkBitmap::kARGB_4444_Config: {
+            const uint16_t* src = fDevice->getAddr16(x, y);
+            if (scale == 256) {
+                for (int i = count - 1; i >= 0; --i) {
+                    span[i] = SkPixel4444ToPixel32(src[i]);
+                }
+            } else {
+                unsigned scale16 = scale >> 4;
+                for (int i = count - 1; i >= 0; --i) {
+                    uint32_t c = SkExpand_4444(src[i]) * scale16;
+                    span[i] = SkCompact_8888(c);
+                }
+            }
+            break;
+        }
+        case SkBitmap::kIndex8_Config:
+            SkDEBUGFAIL("index8 not supported as a destination device");
+            break;
+        case SkBitmap::kA8_Config: {
+            const uint8_t* src = fDevice->getAddr8(x, y);
+            if (scale == 256) {
+                for (int i = count - 1; i >= 0; --i) {
+                    span[i] = SkPackARGB32(src[i], 0, 0, 0);
+                }
+            } else {
+                for (int i = count - 1; i >= 0; --i) {
+                    span[i] = SkPackARGB32(SkAlphaMul(src[i], scale), 0, 0, 0);
+                }
+            }
+            break;
+        }
+        case SkBitmap::kA1_Config:
+            SkDEBUGFAIL("kA1_Config umimplemented at this time");
+            break;
+        default:    // to avoid warnings
+            break;
+    }
+}
+
+void SkTransparentShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
+    SkASSERT(fDevice->getConfig() == SkBitmap::kRGB_565_Config);
+
+    uint16_t* src = fDevice->getAddr16(x, y);
+    if (src != span) {
+        memcpy(span, src, count << 1);
+    }
+}
+
+SkFlattenable::Factory SkTransparentShader::getFactory() {
+    return Create;
+}
+
+void SkTransparentShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+}
+
diff --git a/legacy/src/images/SkBitmapRegionDecoder.cpp b/legacy/src/images/SkBitmapRegionDecoder.cpp
new file mode 100644
index 0000000..24bc446
--- /dev/null
+++ b/legacy/src/images/SkBitmapRegionDecoder.cpp
@@ -0,0 +1,7 @@
+#include "SkBitmapRegionDecoder.h"
+
+bool SkBitmapRegionDecoder::decodeRegion(SkBitmap* bitmap, SkIRect rect,
+        SkBitmap::Config pref, int sampleSize) {
+    fDecoder->setSampleSize(sampleSize);
+    return fDecoder->decodeRegion(bitmap, rect, pref);
+}
diff --git a/src/images/SkBitmap_RLEPixels.h b/legacy/src/images/SkBitmap_RLEPixels.h
similarity index 100%
rename from src/images/SkBitmap_RLEPixels.h
rename to legacy/src/images/SkBitmap_RLEPixels.h
diff --git a/src/images/SkCreateRLEPixelRef.cpp b/legacy/src/images/SkCreateRLEPixelRef.cpp
similarity index 100%
rename from src/images/SkCreateRLEPixelRef.cpp
rename to legacy/src/images/SkCreateRLEPixelRef.cpp
diff --git a/legacy/src/images/SkFDStream.cpp b/legacy/src/images/SkFDStream.cpp
new file mode 100644
index 0000000..e1e214a
--- /dev/null
+++ b/legacy/src/images/SkFDStream.cpp
@@ -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.
+ */
+#include "SkStream.h"
+#include <unistd.h>
+
+//#define TRACE_FDSTREAM
+
+SkFDStream::SkFDStream(int fileDesc, bool closeWhenDone)
+    : fFD(fileDesc), fCloseWhenDone(closeWhenDone) {
+}
+
+SkFDStream::~SkFDStream() {
+    if (fFD >= 0 && fCloseWhenDone) {
+        ::close(fFD);
+    }
+}
+
+bool SkFDStream::rewind() {
+    if (fFD >= 0) {
+        off_t value = ::lseek(fFD, 0, SEEK_SET);
+#ifdef TRACE_FDSTREAM
+        if (value) {
+            SkDebugf("xxxxxxxxxxxxxx rewind failed %d\n", value);
+        }
+#endif
+        return value == 0;
+    }
+    return false;
+}
+
+size_t SkFDStream::read(void* buffer, size_t size) {
+    if (fFD >= 0) {
+        if (buffer == NULL && size == 0) {  // request total size
+            off_t curr = ::lseek(fFD, 0, SEEK_CUR);
+            if (curr < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek failed 0 CURR\n");
+#endif
+                return 0;   // error
+            }
+            off_t size = ::lseek(fFD, 0, SEEK_END);
+            if (size < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek failed 0 END\n");
+#endif
+                size = 0;   // error
+            }
+            if (::lseek(fFD, curr, SEEK_SET) != curr) {
+                // can't restore, error
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek failed %d SET\n", curr);
+#endif
+                return 0;
+            }
+            return size;
+        } else if (NULL == buffer) {        // skip
+            off_t oldCurr = ::lseek(fFD, 0, SEEK_CUR);
+            if (oldCurr < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek1 failed %d CUR\n", oldCurr);
+#endif
+                return 0;   // error;
+            }
+            off_t newCurr = ::lseek(fFD, size, SEEK_CUR);
+            if (newCurr < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek2 failed %d CUR\n", newCurr);
+#endif
+                return 0;   // error;
+            }
+            // return the actual amount we skipped
+            return newCurr - oldCurr;
+        } else {                            // read
+            ssize_t actual = ::read(fFD, buffer, size);
+            // our API can't return an error, so we return 0
+            if (actual < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx read failed %d actual %d\n", size, actual);
+#endif
+                actual = 0;
+            }
+            return actual;
+        }
+    }
+    return 0;
+}
+
diff --git a/legacy/src/images/SkFlipPixelRef.cpp b/legacy/src/images/SkFlipPixelRef.cpp
new file mode 100644
index 0000000..e81c83c
--- /dev/null
+++ b/legacy/src/images/SkFlipPixelRef.cpp
@@ -0,0 +1,133 @@
+
+/*
+ * 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 "SkFlipPixelRef.h"
+#include "SkFlattenable.h"
+#include "SkRegion.h"
+
+SkFlipPixelRef::SkFlipPixelRef(SkBitmap::Config config, int width, int height)
+: fFlipper(width, height) {
+    fConfig = config;
+    fSize = SkBitmap::ComputeSize(config, width, height);
+    fStorage = sk_malloc_throw(fSize << 1);
+    fPage0 = fStorage;
+    fPage1 = (char*)fStorage + fSize;
+}
+
+SkFlipPixelRef::~SkFlipPixelRef() {
+    sk_free(fStorage);
+}
+
+const SkRegion& SkFlipPixelRef::beginUpdate(SkBitmap* device) {
+    void*       writeAddr;
+    const void* readAddr;
+    this->getFrontBack(&readAddr, &writeAddr);
+
+    device->setConfig(fConfig, fFlipper.width(), fFlipper.height());
+    device->setPixels(writeAddr);
+
+    SkRegion    copyBits;
+    const SkRegion& dirty = fFlipper.update(&copyBits);
+
+    SkFlipPixelRef::CopyBitsFromAddr(*device, copyBits, readAddr);
+    return dirty;
+}
+
+void SkFlipPixelRef::endUpdate() {
+    this->swapPages();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void* SkFlipPixelRef::onLockPixels(SkColorTable** ct) {
+    fMutex.acquire();
+    *ct = NULL;
+    return fPage0;
+}
+
+void SkFlipPixelRef::onUnlockPixels() {
+    fMutex.release();
+}
+
+void SkFlipPixelRef::swapPages() {
+    fMutex.acquire();
+    SkTSwap<void*>(fPage0, fPage1);
+    this->notifyPixelsChanged();
+    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,
+                     const void* srcAddr, int shift) {
+    const size_t offset = rect.fTop * dst.rowBytes() + (rect.fLeft << shift);
+    char* dstP = static_cast<char*>(dst.getPixels()) + offset;
+    const char* srcP = static_cast<const char*>(srcAddr) + offset;
+    const size_t rb = dst.rowBytes();
+    const size_t bytes = rect.width() << shift;
+    
+    int height = rect.height();
+    while (--height >= 0) {
+        memcpy(dstP, srcP, bytes);
+        dstP += rb;
+        srcP += rb;
+    }
+}
+
+static int getShift(SkBitmap::Config config) {
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            return 2;
+        case SkBitmap::kRGB_565_Config:
+        case SkBitmap::kARGB_4444_Config:
+            return 1;
+        case SkBitmap::kIndex8_Config:
+        case SkBitmap::kA8_Config:
+            return 0;
+        default:
+            return -1;  // signal not supported
+    }
+}
+
+void SkFlipPixelRef::CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip,
+                                      const void* srcAddr) {
+    const int shift = getShift(dst.config());
+    if (shift < 0) {
+        return;
+    }
+    
+    const SkIRect bounds = {0, 0, dst.width(), dst.height()};
+    SkRegion::Cliperator iter(clip, bounds);
+    
+    while (!iter.done()) {
+        copyRect(dst, iter.rect(), srcAddr, shift);
+        iter.next();
+    }
+}
diff --git a/legacy/src/images/SkImageDecoder.cpp b/legacy/src/images/SkImageDecoder.cpp
new file mode 100644
index 0000000..c4eac9c
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder.cpp
@@ -0,0 +1,268 @@
+
+/*
+ * 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 "SkImageDecoder.h"
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkCanvas.h"
+
+SkVMMemoryReporter::~SkVMMemoryReporter() {
+}
+
+const char *SkImageDecoder::kFormatName[] = {
+    "Unknown Format",
+    "BMP",
+    "GIF",
+    "ICO",
+    "JPEG",
+    "PNG",
+    "WBMP",
+    "WEBP",
+};
+
+static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
+
+SkBitmap::Config SkImageDecoder::GetDeviceConfig()
+{
+    return gDeviceConfig;
+}
+
+void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
+{
+    gDeviceConfig = config;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder::SkImageDecoder()
+    : fReporter(NULL), fPeeker(NULL), fChooser(NULL), fAllocator(NULL),
+      fSampleSize(1), fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
+      fUsePrefTable(false),fPreferQualityOverSpeed(false) {
+}
+
+SkImageDecoder::~SkImageDecoder() {
+    SkSafeUnref(fPeeker);
+    SkSafeUnref(fChooser);
+    SkSafeUnref(fAllocator);
+    SkSafeUnref(fReporter);
+}
+
+SkImageDecoder::Format SkImageDecoder::getFormat() const {
+    return kUnknown_Format;
+}
+
+SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
+    SkRefCnt_SafeAssign(fPeeker, peeker);
+    return peeker;
+}
+
+SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
+    SkRefCnt_SafeAssign(fChooser, chooser);
+    return chooser;
+}
+
+SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
+    SkRefCnt_SafeAssign(fAllocator, alloc);
+    return alloc;
+}
+
+SkVMMemoryReporter* SkImageDecoder::setReporter(SkVMMemoryReporter* reporter) {
+    SkRefCnt_SafeAssign(fReporter, reporter);
+    return reporter;
+}
+
+void SkImageDecoder::setSampleSize(int size) {
+    if (size < 1) {
+        size = 1;
+    }
+    fSampleSize = size;
+}
+
+bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
+                                         int height) const {
+    Chooser* chooser = fChooser;
+
+    if (NULL == chooser) {    // no chooser, we just say YES to decoding :)
+        return true;
+    }
+    chooser->begin(1);
+    chooser->inspect(0, config, width, height);
+    return chooser->choose() == 0;
+}
+
+bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
+                                   SkColorTable* ctable) const {
+    return bitmap->allocPixels(fAllocator, ctable);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
+    if (NULL == pref) {
+        fUsePrefTable = false;
+    } else {
+        fUsePrefTable = true;
+        memcpy(fPrefTable, pref, sizeof(fPrefTable));
+    }
+}
+
+SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
+                                               bool srcHasAlpha) const {
+    SkBitmap::Config config;
+
+    if (fUsePrefTable) {
+        int index = 0;
+        switch (srcDepth) {
+            case kIndex_SrcDepth:
+                index = 0;
+                break;
+            case k16Bit_SrcDepth:
+                index = 2;
+                break;
+            case k32Bit_SrcDepth:
+                index = 4;
+                break;
+        }
+        if (srcHasAlpha) {
+            index += 1;
+        }
+        config = fPrefTable[index];
+    } else {
+        config = fDefaultPref;
+    }
+
+    if (SkBitmap::kNo_Config == config) {
+        config = SkImageDecoder::GetDeviceConfig();
+    }
+    return config;
+}
+
+bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
+                            SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
+    // pass a temporary bitmap, so that if we return false, we are assured of
+    // leaving the caller's bitmap untouched.
+    SkBitmap    tmp;
+
+    // we reset this to false before calling onDecode
+    fShouldCancelDecode = false;
+    // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
+    fDefaultPref = pref;
+
+    if (reuseBitmap) {
+        SkAutoLockPixels alp(*bm);
+        if (bm->getPixels() != NULL) {
+            return this->onDecode(stream, bm, mode);
+        }
+    }
+    if (!this->onDecode(stream, &tmp, mode)) {
+        return false;
+    }
+    bm->swap(tmp);
+    return true;
+}
+
+bool SkImageDecoder::decodeRegion(SkBitmap* bm, SkIRect rect,
+                                  SkBitmap::Config pref) {
+    // we reset this to false before calling onDecodeRegion
+    fShouldCancelDecode = false;
+    // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
+    fDefaultPref = pref;
+
+    if (!this->onDecodeRegion(bm, rect)) {
+        return false;
+    }
+    return true;
+}
+
+bool SkImageDecoder::buildTileIndex(SkStream* stream,
+                                int *width, int *height) {
+    // we reset this to false before calling onBuildTileIndex
+    fShouldCancelDecode = false;
+
+    return this->onBuildTileIndex(stream, width, height);
+}
+
+void SkImageDecoder::cropBitmap(SkBitmap *dest, SkBitmap *src,
+                                    int sampleSize, int destX, int destY,
+                                    int width, int height, int srcX, int srcY) {
+    int w = width / sampleSize;
+    int h = height / sampleSize;
+    // if the destination has no pixels then we must allocate them.
+    if (dest->isNull()) {
+        dest->setConfig(src->getConfig(), w, h);
+        dest->setIsOpaque(src->isOpaque());
+
+        if (!this->allocPixelRef(dest, NULL)) {
+            SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
+            return;
+        }
+    }
+    // check to see if the destination is large enough to decode the desired
+    // region. If this assert fails we will just draw as much of the source
+    // into the destination that we can.
+    SkASSERT(dest->width() >= w && dest->height() >= h);
+
+    // Set the Src_Mode for the paint to prevent transparency issue in the
+    // dest in the event that the dest was being re-used.
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+
+    SkCanvas canvas(*dest);
+    canvas.drawSprite(*src, (srcX - destX) / sampleSize,
+                            (srcY - destY) / sampleSize,
+                            &paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
+                            SkBitmap::Config pref,  Mode mode, Format* format) {
+    SkASSERT(file);
+    SkASSERT(bm);
+
+    SkFILEStream    stream(file);
+    if (stream.isValid()) {
+        if (SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format)) {
+            bm->pixelRef()->setURI(file);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode mode, Format* format) {
+    if (0 == size) {
+        return false;
+    }
+    SkASSERT(buffer);
+
+    SkMemoryStream  stream(buffer, size);
+    return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
+}
+
+bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode mode, Format* format) {
+    SkASSERT(stream);
+    SkASSERT(bm);
+
+    bool success = false;
+    SkImageDecoder* codec = SkImageDecoder::Factory(stream);
+
+    if (NULL != codec) {
+        success = codec->decode(stream, bm, pref, mode);
+        if (success && format) {
+            *format = codec->getFormat();
+        }
+        delete codec;
+    }
+    return success;
+}
diff --git a/legacy/src/images/SkImageDecoder_Factory.cpp b/legacy/src/images/SkImageDecoder_Factory.cpp
new file mode 100644
index 0000000..f3cb120
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_Factory.cpp
@@ -0,0 +1,65 @@
+
+/*
+ * 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 "SkImageDecoder.h"
+#include "SkMovie.h"
+#include "SkStream.h"
+#include "SkTRegistry.h"
+
+typedef SkTRegistry<SkImageDecoder*, SkStream*> DecodeReg;
+
+// N.B. You can't use "DecodeReg::gHead here" due to complex C++
+// corner cases.
+template DecodeReg* SkTRegistry<SkImageDecoder*, SkStream*>::gHead;
+
+#ifdef SK_ENABLE_LIBPNG
+    extern SkImageDecoder* sk_libpng_dfactory(SkStream*);
+#endif
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+    SkImageDecoder* codec = NULL;
+    const DecodeReg* curr = DecodeReg::Head();
+    while (curr) {
+        codec = curr->factory()(stream);
+        // we rewind here, because we promise later when we call "decode", that
+        // the stream will be at its beginning.
+        stream->rewind();
+        if (codec) {
+            return codec;
+        }
+        curr = curr->next();
+    }
+#ifdef SK_ENABLE_LIBPNG
+    codec = sk_libpng_dfactory(stream);
+    stream->rewind();
+    if (codec) {
+        return codec;
+    }
+#endif
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+typedef SkTRegistry<SkMovie*, SkStream*> MovieReg;
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+    const MovieReg* curr = MovieReg::Head();
+    while (curr) {
+        SkMovie* movie = curr->factory()(stream);
+        if (movie) {
+            return movie;
+        }
+        // we must rewind only if we got NULL, since we gave the stream to the
+        // movie, who may have already started reading from it
+        stream->rewind();
+        curr = curr->next();
+    }
+    return NULL;
+}
diff --git a/legacy/src/images/SkImageDecoder_libbmp.cpp b/legacy/src/images/SkImageDecoder_libbmp.cpp
new file mode 100644
index 0000000..6f82def
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_libbmp.cpp
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 2007 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 "bmpdecoderhelper.h"
+#include "SkImageDecoder.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTDArray.h"
+#include "SkTRegistry.h"
+
+class SkBMPImageDecoder : public SkImageDecoder {
+public:
+    SkBMPImageDecoder() {}
+    
+    virtual Format getFormat() const {
+        return kBMP_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+};
+
+static SkImageDecoder* Factory(SkStream* stream) {
+    static const char kBmpMagic[] = { 'B', 'M' };
+    
+    size_t len = stream->getLength();
+    char buffer[sizeof(kBmpMagic)];
+    
+    if (len > sizeof(kBmpMagic) &&
+            stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
+            !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
+        return SkNEW(SkBMPImageDecoder);
+    }
+    return NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
+public:
+    // we don't copy the bitmap, just remember the pointer
+    SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
+
+    // override from BmpDecoderCallback
+    virtual uint8* SetSize(int width, int height) {
+        fWidth = width;
+        fHeight = height;
+        if (fJustBounds) {
+            return NULL;
+        }
+        
+        fRGB.setCount(width * height * 3);  // 3 == r, g, b
+        return fRGB.begin();
+    }
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    uint8_t* rgb() const { return fRGB.begin(); }
+
+private:
+    SkTDArray<uint8_t> fRGB;
+    int fWidth;
+    int fHeight;
+    bool fJustBounds;
+};
+
+bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+
+    size_t length = stream->getLength();
+    SkAutoMalloc storage(length);
+    
+    if (stream->read(storage.get(), length) != length) {
+        return false;
+    }
+    
+    const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
+    SkBmpDecoderCallback callback(justBounds);
+
+    // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
+    {
+        image_codec::BmpDecoderHelper helper;
+        const int max_pixels = 16383*16383; // max width*height
+        if (!helper.DecodeImage((const char*)storage.get(), length,
+                                max_pixels, &callback)) {
+            return false;
+        }
+    }
+    
+    // we don't need this anymore, so free it now (before we try to allocate
+    // the bitmap's pixels) rather than waiting for its destructor
+    storage.free();
+    
+    int width = callback.width();
+    int height = callback.height();
+    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
+
+    // only accept prefConfig if it makes sense for us
+    if (SkBitmap::kARGB_4444_Config != config &&
+            SkBitmap::kRGB_565_Config != config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+
+    SkScaledBitmapSampler sampler(width, height, getSampleSize());
+
+    if (justBounds) {
+        bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+        bm->setIsOpaque(true);
+        return true;
+    }
+#ifdef SK_BUILD_FOR_ANDROID
+    // No Bitmap reuse supported for this format
+    if (!bm->isNull()) {
+        return false;
+    }
+#endif
+    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+    bm->setIsOpaque(true);
+
+    if (!this->allocPixelRef(bm, NULL)) {
+        return false;
+    }
+    
+    SkAutoLockPixels alp(*bm);
+    
+    if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
+        return false;
+    }
+
+    const int srcRowBytes = width * 3;
+    const int dstHeight = sampler.scaledHeight();
+    const uint8_t* srcRow = callback.rgb();
+    
+    srcRow += sampler.srcY0() * srcRowBytes;
+    for (int y = 0; y < dstHeight; y++) {
+        sampler.next(srcRow);
+        srcRow += sampler.srcDY() * srcRowBytes;
+    }
+    return true;
+}
diff --git a/legacy/src/images/SkImageDecoder_libgif.cpp b/legacy/src/images/SkImageDecoder_libgif.cpp
new file mode 100644
index 0000000..2975478
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_libgif.cpp
@@ -0,0 +1,353 @@
+
+/*
+ * 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 "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkPackBits.h"
+
+#include "gif_lib.h"
+
+class SkGIFImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kGIF_Format;
+    }
+    
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+};
+
+static const uint8_t gStartingIterlaceYValue[] = {
+    0, 4, 2, 1
+};
+static const uint8_t gDeltaIterlaceYValue[] = {
+    8, 8, 4, 2
+};
+
+/*  Implement the GIF interlace algorithm in an iterator.
+    1) grab every 8th line beginning at 0
+    2) grab every 8th line beginning at 4
+    3) grab every 4th line beginning at 2
+    4) grab every 2nd line beginning at 1
+*/
+class GifInterlaceIter {
+public:
+    GifInterlaceIter(int height) : fHeight(height) {
+        fStartYPtr = gStartingIterlaceYValue;
+        fDeltaYPtr = gDeltaIterlaceYValue;
+
+        fCurrY = *fStartYPtr++;
+        fDeltaY = *fDeltaYPtr++;
+    }
+    
+    int currY() const {
+        SkASSERT(fStartYPtr);
+        SkASSERT(fDeltaYPtr);
+        return fCurrY;
+    }
+
+    void next() {
+        SkASSERT(fStartYPtr);
+        SkASSERT(fDeltaYPtr);
+
+        int y = fCurrY + fDeltaY;
+        // We went from an if statement to a while loop so that we iterate
+        // through fStartYPtr until a valid row is found. This is so that images
+        // that are smaller than 5x5 will not trash memory.
+        while (y >= fHeight) {
+            if (gStartingIterlaceYValue +
+                    SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
+                // we done
+                SkDEBUGCODE(fStartYPtr = NULL;)
+                SkDEBUGCODE(fDeltaYPtr = NULL;)
+                y = 0;
+            } else {
+                y = *fStartYPtr++;
+                fDeltaY = *fDeltaYPtr++;
+            }
+        }
+        fCurrY = y;
+    }
+    
+private:
+    const int fHeight;
+    int fCurrY;
+    int fDeltaY;
+    const uint8_t* fStartYPtr;
+    const uint8_t* fDeltaYPtr;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+//#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
+//#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)
+
+static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
+                              int size) {
+    SkStream* stream = (SkStream*) fileType->UserData;
+    return (int) stream->read(out, size);
+}
+
+void CheckFreeExtension(SavedImage* Image) {
+    if (Image->ExtensionBlocks) {
+        FreeExtension(Image);
+    }
+}
+
+// return NULL on failure
+static const ColorMapObject* find_colormap(const GifFileType* gif) {
+    const ColorMapObject* cmap = gif->Image.ColorMap;
+    if (NULL == cmap) {
+        cmap = gif->SColorMap;
+    }
+
+    if (NULL == cmap) {
+        // no colormap found
+        return NULL;
+    }
+    // some sanity checks
+    if (cmap && ((unsigned)cmap->ColorCount > 256 ||
+                 cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
+        cmap = NULL;
+    }
+    return cmap;
+}
+
+// return -1 if not found (i.e. we're completely opaque)
+static int find_transpIndex(const SavedImage& image, int colorCount) {
+    int transpIndex = -1;
+    for (int i = 0; i < image.ExtensionBlockCount; ++i) {
+        const ExtensionBlock* eb = image.ExtensionBlocks + i;
+        if (eb->Function == 0xF9 && eb->ByteCount == 4) {
+            if (eb->Bytes[0] & 1) {
+                transpIndex = (unsigned char)eb->Bytes[3];
+                // check for valid transpIndex
+                if (transpIndex >= colorCount) {
+                    transpIndex = -1;
+                }
+                break;
+            }
+        }
+    }
+    return transpIndex;
+}
+
+static bool error_return(GifFileType* gif, const SkBitmap& bm,
+                         const char msg[]) {
+#if 0
+    SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
+             msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
+#endif
+    return false;
+}
+
+bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
+    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
+    if (NULL == gif) {
+        return error_return(gif, *bm, "DGifOpen");
+    }
+
+    SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
+
+    SavedImage temp_save;
+    temp_save.ExtensionBlocks=NULL;
+    temp_save.ExtensionBlockCount=0;
+    SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
+
+    int width, height;
+    GifRecordType recType;
+    GifByteType *extData;
+    int transpIndex = -1;   // -1 means we don't have it (yet)
+    
+    do {
+        if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
+            return error_return(gif, *bm, "DGifGetRecordType");
+        }
+        
+        switch (recType) {
+        case IMAGE_DESC_RECORD_TYPE: {
+            if (DGifGetImageDesc(gif) == GIF_ERROR) {
+                return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
+            }
+            
+            if (gif->ImageCount < 1) {    // sanity check
+                return error_return(gif, *bm, "ImageCount < 1");
+            }
+                
+            width = gif->SWidth;
+            height = gif->SHeight;
+            if (width <= 0 || height <= 0 ||
+                !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
+                                           width, height)) {
+                return error_return(gif, *bm, "chooseFromOneChoice");
+            }
+            
+            if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+                bm->setConfig(SkBitmap::kIndex8_Config, width, height);
+                return true;
+            }
+#ifdef SK_BUILD_FOR_ANDROID
+            // No Bitmap reuse supported for this format
+            if (!bm->isNull()) {
+                return false;
+            }
+#endif
+
+            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
+            SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
+            const GifImageDesc& desc = image->ImageDesc;
+            
+            // check for valid descriptor
+            if (   (desc.Top | desc.Left) < 0 ||
+                    desc.Left + desc.Width > width ||
+                    desc.Top + desc.Height > height) {
+                return error_return(gif, *bm, "TopLeft");
+            }
+            
+            // now we decode the colortable
+            int colorCount = 0;
+            {
+                const ColorMapObject* cmap = find_colormap(gif);
+                if (NULL == cmap) {
+                    return error_return(gif, *bm, "null cmap");
+                }
+
+                colorCount = cmap->ColorCount;
+                SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
+                SkPMColor* colorPtr = ctable->lockColors();
+                for (int index = 0; index < colorCount; index++)
+                    colorPtr[index] = SkPackARGB32(0xFF,
+                                                   cmap->Colors[index].Red, 
+                                                   cmap->Colors[index].Green,
+                                                   cmap->Colors[index].Blue);
+
+                transpIndex = find_transpIndex(temp_save, colorCount);
+                if (transpIndex < 0)
+                    ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+                else
+                    colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
+                ctable->unlockColors(true);
+
+                SkAutoUnref aurts(ctable);
+                if (!this->allocPixelRef(bm, ctable)) {
+                    return error_return(gif, *bm, "allocPixelRef");
+                }
+            }
+            
+            SkAutoLockPixels alp(*bm);
+
+            // time to decode the scanlines
+            //
+            uint8_t*  scanline = bm->getAddr8(0, 0);
+            const int rowBytes = bm->rowBytes();
+            const int innerWidth = desc.Width;
+            const int innerHeight = desc.Height;
+
+            // abort if either inner dimension is <= 0
+            if (innerWidth <= 0 || innerHeight <= 0) {
+                return error_return(gif, *bm, "non-pos inner width/height");
+            }
+
+            // are we only a subset of the total bounds?
+            if ((desc.Top | desc.Left) > 0 ||
+                 innerWidth < width || innerHeight < height)
+            {
+                int fill;
+                if (transpIndex >= 0) {
+                    fill = transpIndex;
+                } else {
+                    fill = gif->SBackGroundColor;
+                }
+                // check for valid fill index/color
+                if (static_cast<unsigned>(fill) >=
+                        static_cast<unsigned>(colorCount)) {
+                    fill = 0;
+                }
+                memset(scanline, fill, bm->getSize());
+                // bump our starting address
+                scanline += desc.Top * rowBytes + desc.Left;
+            }
+            
+            // now decode each scanline
+            if (gif->Image.Interlace)
+            {
+                GifInterlaceIter iter(innerHeight);
+                for (int y = 0; y < innerHeight; y++)
+                {
+                    uint8_t* row = scanline + iter.currY() * rowBytes;
+                    if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
+                        return error_return(gif, *bm, "interlace DGifGetLine");
+                    }
+                    iter.next();
+                }
+            }
+            else
+            {
+                // easy, non-interlace case
+                for (int y = 0; y < innerHeight; y++) {
+                    if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
+                        return error_return(gif, *bm, "DGifGetLine");
+                    }
+                    scanline += rowBytes;
+                }
+            }
+            goto DONE;
+            } break;
+            
+        case EXTENSION_RECORD_TYPE:
+            if (DGifGetExtension(gif, &temp_save.Function,
+                                 &extData) == GIF_ERROR) {
+                return error_return(gif, *bm, "DGifGetExtension");
+            }
+
+            while (extData != NULL) {
+                /* Create an extension block with our data */
+                if (AddExtensionBlock(&temp_save, extData[0],
+                                      &extData[1]) == GIF_ERROR) {
+                    return error_return(gif, *bm, "AddExtensionBlock");
+                }
+                if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
+                    return error_return(gif, *bm, "DGifGetExtensionNext");
+                }
+                temp_save.Function = 0;
+            }
+            break;
+            
+        case TERMINATE_RECORD_TYPE:
+            break;
+            
+        default:	/* Should be trapped by DGifGetRecordType */
+            break;
+        }
+    } while (recType != TERMINATE_RECORD_TYPE);
+
+DONE:
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* Factory(SkStream* stream) {
+    char buf[GIF_STAMP_LEN];
+    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+            return SkNEW(SkGIFImageDecoder);
+        }
+    }
+    return NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
diff --git a/legacy/src/images/SkImageDecoder_libico.cpp b/legacy/src/images/SkImageDecoder_libico.cpp
new file mode 100644
index 0000000..25a5078
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_libico.cpp
@@ -0,0 +1,396 @@
+
+/*
+ * 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 "SkImageDecoder.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTypes.h"
+
+class SkICOImageDecoder : public SkImageDecoder {
+public:
+    SkICOImageDecoder();
+    
+    virtual Format getFormat() const {
+        return kICO_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+};
+
+SkImageDecoder* SkCreateICOImageDecoder();
+SkImageDecoder* SkCreateICOImageDecoder() {
+    return new SkICOImageDecoder;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+//read bytes starting from the begin-th index in the buffer
+//read in Intel order, and return an integer
+
+#define readByte(buffer,begin) buffer[begin]
+#define read2Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)
+#define read4Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)+(buffer[begin+2]<<16)+(buffer[begin+3]<<24)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+SkICOImageDecoder::SkICOImageDecoder()
+{
+}
+
+//helpers - my function pointer will call one of these, depending on the bitCount, each time through the inner loop
+static void editPixelBit1(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit4(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit8(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit24(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit32(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+            
+            
+static int calculateRowBytesFor8888(int w, int bitCount)
+{
+    //  Default rowBytes is w << 2 for kARGB_8888
+    //  In the case of a 4 bit image with an odd width, we need to add some
+    //  so we can go off the end of the drawn bitmap.
+    //  Add 4 to ensure that it is still a multiple of 4.
+    if (4 == bitCount && (w & 0x1)) {
+        return (w + 1) << 2;
+    }
+    //  Otherwise return 0, which will allow it to be calculated automatically.
+    return 0;
+}
+
+bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode)
+{
+    size_t length = stream->read(NULL, 0);
+    SkAutoMalloc autoMal(length);
+    unsigned char* buf = (unsigned char*)autoMal.get();
+    if (stream->read((void*)buf, length) != length) {
+        return false;
+    }
+    
+    //these should always be the same - should i use for error checking? - what about files that have some
+    //incorrect values, but still decode properly?
+    int reserved = read2Bytes(buf, 0);    // 0
+    int type = read2Bytes(buf, 2);        // 1
+    if (reserved != 0 || type != 1)
+        return false;
+    int count = read2Bytes(buf, 4);
+    
+    //need to at least have enough space to hold the initial table of info
+    if (length < (size_t)(6 + count*16))
+        return false;
+        
+    int choice;
+    Chooser* chooser = this->getChooser();
+    //FIXME:if no chooser, consider providing the largest color image
+    //what are the odds that the largest image would be monochrome?
+    if (NULL == chooser) {
+        choice = 0;
+    } else {
+        chooser->begin(count);
+        for (int i = 0; i < count; i++)
+        {
+            //need to find out the config, width, and height from the stream
+            int width = readByte(buf, 6 + i*16);
+            int height = readByte(buf, 7 + i*16);
+            int offset = read4Bytes(buf, 18 + i*16);
+            int bitCount = read2Bytes(buf, offset+14);
+            SkBitmap::Config c;
+            //currently only provide ARGB_8888_, but maybe we want kIndex8_Config for 1 and 4, and possibly 8?
+            //or maybe we'll determine this based on the provided config
+            switch (bitCount)
+            {
+                case 1:
+                case 4:
+                    // In reality, at least for the moment, these will be decoded into kARGB_8888 bitmaps.
+                    // However, this will be used to distinguish between the lower quality 1bpp and 4 bpp 
+                    // images and the higher quality images.
+                    c = SkBitmap::kIndex8_Config;
+                    break;
+                case 8:
+                case 24:
+                case 32:
+                    c = SkBitmap::kARGB_8888_Config;
+                    break;
+                default:
+                    SkDEBUGF(("Image with %ibpp not supported\n", bitCount));
+                    continue;
+            }
+            chooser->inspect(i, c, width, height);
+        }
+        choice = chooser->choose();
+    }
+    
+    //you never know what the chooser is going to supply
+    if (choice >= count || choice < 0)       
+        return false;
+    
+    //skip ahead to the correct header
+    //commented out lines are not used, but if i switch to other read method, need to know how many to skip
+    //otherwise, they could be used for error checking
+    int w = readByte(buf, 6 + choice*16);
+    int h = readByte(buf, 7 + choice*16);
+    int colorCount = readByte(buf, 8 + choice*16);
+    //int reservedToo = readByte(buf, 9 + choice*16);   //0
+    //int planes = read2Bytes(buf, 10 + choice*16);       //1 - but often 0
+    //int fakeBitCount = read2Bytes(buf, 12 + choice*16); //should be real - usually 0
+    int size = read4Bytes(buf, 14 + choice*16);           //matters?
+    int offset = read4Bytes(buf, 18 + choice*16);
+    if ((size_t)(offset + size) > length)
+        return false;
+    //int infoSize = read4Bytes(buf, offset);             //40
+    //int width = read4Bytes(buf, offset+4);              //should == w
+    //int height = read4Bytes(buf, offset+8);             //should == 2*h
+    //int planesToo = read2Bytes(buf, offset+12);         //should == 1 (does it?)
+    int bitCount = read2Bytes(buf, offset+14);
+    
+    void (*placePixel)(const int pixelNo, const unsigned char* buf, 
+        const int xorOffset, int& x, int y, const int w, 
+        SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) = NULL;
+    switch (bitCount)
+    {
+        case 1:
+            placePixel = &editPixelBit1;
+            colorCount = 2;
+            break;
+        case 4:
+            placePixel = &editPixelBit4;
+            colorCount = 16;
+            break;
+        case 8:
+            placePixel = &editPixelBit8;
+            colorCount = 256;
+            break;
+        case 24:
+            placePixel = &editPixelBit24;
+            colorCount = 0;
+            break;
+        case 32:
+            placePixel = &editPixelBit32;
+            colorCount = 0;
+            break;
+        default:
+            SkDEBUGF(("Decoding %ibpp is unimplemented\n", bitCount));
+            return false;
+    }
+        
+    //these should all be zero, but perhaps are not - need to check
+    //int compression = read4Bytes(buf, offset+16);       //0
+    //int imageSize = read4Bytes(buf, offset+20);         //0 - sometimes has a value
+    //int xPixels = read4Bytes(buf, offset+24);           //0
+    //int yPixels = read4Bytes(buf, offset+28);           //0
+    //int colorsUsed = read4Bytes(buf, offset+32)         //0 - might have an actual value though
+    //int colorsImportant = read4Bytes(buf, offset+36);   //0
+        
+    int begin = offset + 40;
+    //this array represents the colortable
+    //if i allow other types of bitmaps, it may actually be used as a part of the bitmap
+    SkPMColor* colors = NULL;
+    int blue, green, red;
+    if (colorCount) 
+    {
+        colors = new SkPMColor[colorCount];
+        for (int j = 0; j < colorCount; j++)
+        {
+            //should this be a function - maybe a #define?
+            blue = readByte(buf, begin + 4*j);
+            green = readByte(buf, begin + 4*j + 1);
+            red = readByte(buf, begin + 4*j + 2);
+            colors[j] = SkPackARGB32(0xFF, red & 0xFF, green & 0xFF, blue & 0xFF);
+        }
+    }
+    int bitWidth = w*bitCount;
+    int test = bitWidth & 0x1F;
+    int mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1);    //either 0xFFFFFFFF or 0
+    int lineBitWidth = (bitWidth & 0xFFFFFFE0) + (0x20 & mask);
+    int lineWidth = lineBitWidth/bitCount;
+    
+    int xorOffset = begin + colorCount*4;   //beginning of the color bitmap
+                                            //other read method means we will just be here already
+    int andOffset = xorOffset + ((lineWidth*h*bitCount) >> 3);
+    
+    /*int */test = w & 0x1F;   //the low 5 bits - we are rounding up to the next 32 (2^5)
+    /*int */mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1);    //either 0xFFFFFFFF or 0
+    int andLineWidth = (w & 0xFFFFFFE0) + (0x20 & mask);
+    //if we allow different Configs, everything is the same til here
+    //change the config, and use different address getter, and place index vs color, and add the color table
+    //FIXME: what is the tradeoff in size?
+    //if the andbitmap (mask) is all zeroes, then we can easily do an index bitmap
+    //however, with small images with large colortables, maybe it's better to still do argb_8888
+
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
+        delete[] colors;
+        return true;
+    }
+#ifdef SK_BUILD_FOR_ANDROID
+    // No Bitmap reuse supported for this format
+    if (!bm->isNull()) {
+        return false;
+    }
+#endif
+    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
+
+    if (!this->allocPixelRef(bm, NULL))
+    {
+        delete[] colors;
+        return false;
+    }
+    
+    SkAutoLockPixels alp(*bm);
+
+    for (int y = 0; y < h; y++)
+    {
+        for (int x = 0; x < w; x++)
+        {
+            //U32* address = bm->getAddr32(x, y);
+            
+            //check the alpha bit first, but pass it along to the function to figure out how to deal with it
+            int andPixelNo = andLineWidth*(h-y-1)+x;
+            //only need to get a new alphaByte when x %8 == 0
+            //but that introduces an if and a mod - probably much slower
+            //that's ok, it's just a read of an array, not a stream
+            int alphaByte = readByte(buf, andOffset + (andPixelNo >> 3));
+            int shift = 7 - (andPixelNo & 0x7);
+            int m = 1 << shift;
+            
+            int pixelNo = lineWidth*(h-y-1)+x;
+            placePixel(pixelNo, buf, xorOffset, x, y, w, bm, alphaByte, m, shift, colors);
+
+        }
+    }
+
+    delete [] colors;
+    //ensure we haven't read off the end?
+    //of course this doesn't help us if the andOffset was a lie...
+    //return andOffset + (andLineWidth >> 3) <= length;
+    return true;
+}   //onDecode
+
+//function to place the pixel, determined by the bitCount
+static void editPixelBit1(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    // note that this should be the same as/similar to the AND bitmap
+    SkPMColor* address = bm->getAddr32(x,y);
+    int byte = readByte(buf, xorOffset + (pixelNo >> 3));
+    int colorBit;
+    int alphaBit;
+    // Read all of the bits in this byte.  
+    int i = x + 8;
+    // Pin to the width so we do not write outside the bounds of 
+    // our color table.
+    i = i > w ? w : i;
+    // While loop to check all 8 bits individually.
+    while (x < i)
+    {
+        
+        colorBit = (byte & m) >> shift;
+        alphaBit = (alphaByte & m) >> shift;
+        *address = (alphaBit-1)&(colors[colorBit]);
+        x++;
+        // setup for the next pixel
+        address = address + 1;
+        m = m >> 1;
+        shift -= 1;
+    }
+    x--;
+}
+static void editPixelBit4(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int byte = readByte(buf, xorOffset + (pixelNo >> 1));
+    int pixel = (byte >> 4) & 0xF;
+    int alphaBit = (alphaByte & m) >> shift;
+    *address = (alphaBit-1)&(colors[pixel]);
+    x++;
+    //if w is odd, x may be the same as w, which means we are writing to an unused portion of the bitmap
+    //but that's okay, since i've added an extra rowByte for just this purpose
+    address = address + 1;
+    pixel = byte & 0xF;
+    m = m >> 1;
+    alphaBit = (alphaByte & m) >> (shift-1);
+    //speed up trick here
+    *address = (alphaBit-1)&(colors[pixel]);
+}
+
+static void editPixelBit8(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int pixel = readByte(buf, xorOffset + pixelNo);
+    int alphaBit = (alphaByte & m) >> shift;
+    *address = (alphaBit-1)&(colors[pixel]);
+}            
+
+static void editPixelBit24(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int blue = readByte(buf, xorOffset + 3*pixelNo);
+    int green = readByte(buf, xorOffset + 3*pixelNo + 1);
+    int red = readByte(buf, xorOffset + 3*pixelNo + 2);
+    int alphaBit = (alphaByte & m) >> shift;
+    //alphaBit == 1 => alpha = 0
+    int alpha = (alphaBit-1) & 0xFF;
+    *address = SkPreMultiplyARGB(alpha, red, green, blue);    
+}
+
+static void editPixelBit32(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int blue = readByte(buf, xorOffset + 4*pixelNo);
+    int green = readByte(buf, xorOffset + 4*pixelNo + 1);
+    int red = readByte(buf, xorOffset + 4*pixelNo + 2);
+    int alphaBit = (alphaByte & m) >> shift;
+#if 1 // don't trust the alphaBit for 32bit images <mrr>
+    alphaBit = 0;
+#endif
+    int alpha = readByte(buf, xorOffset + 4*pixelNo + 3) & ((alphaBit-1)&0xFF);
+    *address = SkPreMultiplyARGB(alpha, red, green, blue);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* Factory(SkStream* stream) {
+    // Check to see if the first four bytes are 0,0,1,0
+    // FIXME: Is that required and sufficient?
+    SkAutoMalloc autoMal(4);
+    unsigned char* buf = (unsigned char*)autoMal.get();
+    stream->read((void*)buf, 4);
+    int reserved = read2Bytes(buf, 0);
+    int type = read2Bytes(buf, 2);
+    if (reserved != 0 || type != 1) {
+        // This stream does not represent an ICO image.
+        return NULL;
+    }
+    return SkNEW(SkICOImageDecoder);
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+
diff --git a/legacy/src/images/SkImageDecoder_libjpeg.cpp b/legacy/src/images/SkImageDecoder_libjpeg.cpp
new file mode 100644
index 0000000..33f222c
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_libjpeg.cpp
@@ -0,0 +1,1013 @@
+
+/*
+ * Copyright 2007 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 "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkJpegUtility.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkRect.h"
+#include "SkCanvas.h"
+
+#include <stdio.h>
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+#include <cutils/properties.h>
+
+// Key to lookup the size of memory buffer set in system property
+static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap";
+#endif
+
+// this enables timing code to report milliseconds for an encode
+//#define TIME_ENCODE
+//#define TIME_DECODE
+
+// this enables our rgb->yuv code, which is faster than libjpeg on ARM
+// disable for the moment, as we have some glitches when width != multiple of 4
+#define WE_CONVERT_TO_YUV
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+class SkJPEGImageIndex {
+public:
+    SkJPEGImageIndex() {}
+    virtual ~SkJPEGImageIndex() {
+        jpeg_destroy_huffman_index(index);
+        jpeg_finish_decompress(cinfo);
+        jpeg_destroy_decompress(cinfo);
+        delete cinfo->src;
+        free(cinfo);
+    }
+    jpeg_decompress_struct *cinfo;
+    huffman_index *index;
+};
+
+
+class SkJPEGImageDecoder : public SkImageDecoder {
+public:
+    SkJPEGImageDecoder() {
+        index = NULL;
+    }
+    ~SkJPEGImageDecoder() {
+        if (index)
+            delete index;
+    }
+    virtual Format getFormat() const {
+        return kJPEG_Format;
+    }
+protected:
+    virtual bool onBuildTileIndex(SkStream *stream,
+                                int *width, int *height);
+    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+private:
+    SkJPEGImageIndex *index;
+    int imageWidth;
+    int imageHeight;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+
+class AutoTimeMillis {
+public:
+    AutoTimeMillis(const char label[]) : fLabel(label) {
+        if (!fLabel) {
+            fLabel = "";
+        }
+        fNow = SkTime::GetMSecs();
+    }
+    ~AutoTimeMillis() {
+        SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
+    }
+private:
+    const char* fLabel;
+    SkMSec      fNow;
+};
+
+/* Automatically clean up after throwing an exception */
+class JPEGAutoClean {
+public:
+    JPEGAutoClean(): cinfo_ptr(NULL) {}
+    ~JPEGAutoClean() {
+        if (cinfo_ptr) {
+            jpeg_destroy_decompress(cinfo_ptr);
+        }
+    }
+    void set(jpeg_decompress_struct* info) {
+        cinfo_ptr = info;
+    }
+private:
+    jpeg_decompress_struct* cinfo_ptr;
+};
+
+#ifdef SK_BUILD_FOR_ANDROID
+/* Check if the memory cap property is set.
+   If so, use the memory size for jpeg decode.
+*/
+static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
+#ifdef ANDROID_LARGE_MEMORY_DEVICE
+    cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
+#else
+    cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
+#endif
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  If we need to better match the request, we might examine the image and
+     output dimensions, and determine if the downsampling jpeg provided is
+     not sufficient. If so, we can recompute a modified sampleSize value to
+     make up the difference.
+
+     To skip this additional scaling, just set sampleSize = 1; below.
+ */
+static int recompute_sampleSize(int sampleSize,
+                                const jpeg_decompress_struct& cinfo) {
+    return sampleSize * cinfo.output_width / cinfo.image_width;
+}
+
+static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
+    /* These are initialized to 0, so if they have non-zero values, we assume
+       they are "valid" (i.e. have been computed by libjpeg)
+     */
+    return cinfo.output_width != 0 && cinfo.output_height != 0;
+}
+
+static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
+                          int count) {
+    for (int i = 0; i < count; i++) {
+        JSAMPLE* rowptr = (JSAMPLE*)buffer;
+        int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
+        if (row_count != 1) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
+                          huffman_index *index, void* buffer,
+                          int count) {
+    for (int i = 0; i < count; i++) {
+        JSAMPLE* rowptr = (JSAMPLE*)buffer;
+        int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
+        if (row_count != 1) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// This guy exists just to aid in debugging, as it allows debuggers to just
+// set a break-point in one place to see all error exists.
+static bool return_false(const jpeg_decompress_struct& cinfo,
+                         const SkBitmap& bm, const char msg[]) {
+#ifdef SK_DEBUG
+    SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
+             cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
+             bm.width(), bm.height());
+#endif
+    return false;   // must always return false
+}
+
+bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+#ifdef TIME_DECODE
+    AutoTimeMillis atm("JPEG Decode");
+#endif
+
+    SkAutoMalloc  srcStorage;
+    JPEGAutoClean autoClean;
+
+    jpeg_decompress_struct  cinfo;
+    skjpeg_error_mgr        sk_err;
+    skjpeg_source_mgr       sk_stream(stream, this, false);
+
+    cinfo.err = jpeg_std_error(&sk_err);
+    sk_err.error_exit = skjpeg_error_exit;
+
+    // All objects need to be instantiated before this setjmp call so that
+    // they will be cleaned up properly if an error occurs.
+    if (setjmp(sk_err.fJmpBuf)) {
+        return return_false(cinfo, *bm, "setjmp");
+    }
+
+    jpeg_create_decompress(&cinfo);
+    autoClean.set(&cinfo);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    overwrite_mem_buffer_size(&cinfo);
+#endif
+
+    //jpeg_stdio_src(&cinfo, file);
+    cinfo.src = &sk_stream;
+
+    int status = jpeg_read_header(&cinfo, true);
+    if (status != JPEG_HEADER_OK) {
+        return return_false(cinfo, *bm, "read_header");
+    }
+
+    /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it
+        can) much faster that we, just use their num/denom api to approximate
+        the size.
+    */
+    int sampleSize = this->getSampleSize();
+
+    if (this->getPreferQualityOverSpeed()) {
+        cinfo.dct_method = JDCT_ISLOW;
+    } else {
+        cinfo.dct_method = JDCT_IFAST;
+    }
+
+    cinfo.scale_num = 1;
+    cinfo.scale_denom = sampleSize;
+
+    /* this gives about 30% performance improvement. In theory it may
+       reduce the visual quality, in practice I'm not seeing a difference
+     */
+    cinfo.do_fancy_upsampling = 0;
+
+    /* this gives another few percents */
+    cinfo.do_block_smoothing = 0;
+
+    /* default format is RGB */
+    cinfo.out_color_space = JCS_RGB;
+
+    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
+    // only these make sense for jpegs
+    if (config != SkBitmap::kARGB_8888_Config &&
+        config != SkBitmap::kARGB_4444_Config &&
+        config != SkBitmap::kRGB_565_Config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+
+#ifdef ANDROID_RGB
+    cinfo.dither_mode = JDITHER_NONE;
+    if (config == SkBitmap::kARGB_8888_Config) {
+        cinfo.out_color_space = JCS_RGBA_8888;
+    } else if (config == SkBitmap::kRGB_565_Config) {
+        cinfo.out_color_space = JCS_RGB_565;
+        if (this->getDitherImage()) {
+            cinfo.dither_mode = JDITHER_ORDERED;
+        }
+    }
+#endif
+
+    if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) {
+        bm->setConfig(config, cinfo.image_width, cinfo.image_height);
+        bm->setIsOpaque(true);
+        return true;
+    }
+
+    /*  image_width and image_height are the original dimensions, available
+        after jpeg_read_header(). To see the scaled dimensions, we have to call
+        jpeg_start_decompress(), and then read output_width and output_height.
+    */
+    if (!jpeg_start_decompress(&cinfo)) {
+        /*  If we failed here, we may still have enough information to return
+            to the caller if they just wanted (subsampled bounds). If sampleSize
+            was 1, then we would have already returned. Thus we just check if
+            we're in kDecodeBounds_Mode, and that we have valid output sizes.
+
+            One reason to fail here is that we have insufficient stream data
+            to complete the setup. However, output dimensions seem to get
+            computed very early, which is why this special check can pay off.
+         */
+        if (SkImageDecoder::kDecodeBounds_Mode == mode &&
+                valid_output_dimensions(cinfo)) {
+            SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
+                                       recompute_sampleSize(sampleSize, cinfo));
+            bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
+            bm->setIsOpaque(true);
+            return true;
+        } else {
+            return return_false(cinfo, *bm, "start_decompress");
+        }
+    }
+    sampleSize = recompute_sampleSize(sampleSize, cinfo);
+
+    // should we allow the Chooser (if present) to pick a config for us???
+    if (!this->chooseFromOneChoice(config, cinfo.output_width,
+                                   cinfo.output_height)) {
+        return return_false(cinfo, *bm, "chooseFromOneChoice");
+    }
+
+#ifdef ANDROID_RGB
+    /* short-circuit the SkScaledBitmapSampler when possible, as this gives
+       a significant performance boost.
+    */
+    if (sampleSize == 1 &&
+        ((config == SkBitmap::kARGB_8888_Config && 
+                cinfo.out_color_space == JCS_RGBA_8888) ||
+        (config == SkBitmap::kRGB_565_Config && 
+                cinfo.out_color_space == JCS_RGB_565)))
+    {
+        bm->lockPixels();
+        JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+        bm->unlockPixels();
+        bool reuseBitmap = (rowptr != NULL);
+        if (reuseBitmap && ((int) cinfo.output_width != bm->width() ||
+                (int) cinfo.output_height != bm->height())) {
+            // Dimensions must match
+            return false;
+        }
+
+        if (!reuseBitmap) {
+            bm->setConfig(config, cinfo.output_width, cinfo.output_height);
+            bm->setIsOpaque(true);
+            if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+                return true;
+            }
+            if (!this->allocPixelRef(bm, NULL)) {
+                return return_false(cinfo, *bm, "allocPixelRef");
+            }
+        } else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+            return true;
+        }
+        SkAutoLockPixels alp(*bm);
+        rowptr = (JSAMPLE*)bm->getPixels();
+        INT32 const bpr =  bm->rowBytes();
+        
+        while (cinfo.output_scanline < cinfo.output_height) {
+            int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+            // if row_count == 0, then we didn't get a scanline, so abort.
+            // if we supported partial images, we might return true in this case
+            if (0 == row_count) {
+                return return_false(cinfo, *bm, "read_scanlines");
+            }
+            if (this->shouldCancelDecode()) {
+                return return_false(cinfo, *bm, "shouldCancelDecode");
+            }
+            rowptr += bpr;
+        }
+        if (reuseBitmap) {
+            bm->notifyPixelsChanged();
+        }
+        jpeg_finish_decompress(&cinfo);
+        return true;
+    }
+#endif
+    
+    // check for supported formats
+    SkScaledBitmapSampler::SrcConfig sc;
+    if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
+        sc = SkScaledBitmapSampler::kRGB;
+#ifdef ANDROID_RGB
+    } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
+        sc = SkScaledBitmapSampler::kRGBX;
+    } else if (JCS_RGB_565 == cinfo.out_color_space) {
+        sc = SkScaledBitmapSampler::kRGB_565;
+#endif
+    } else if (1 == cinfo.out_color_components &&
+               JCS_GRAYSCALE == cinfo.out_color_space) {
+        sc = SkScaledBitmapSampler::kGray;
+    } else {
+        return return_false(cinfo, *bm, "jpeg colorspace");
+    }
+
+    SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
+                                  sampleSize);
+
+    bm->lockPixels();
+    JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+    bool reuseBitmap = (rowptr != NULL);
+    bm->unlockPixels();
+    if (reuseBitmap && (sampler.scaledWidth() != bm->width() ||
+            sampler.scaledHeight() != bm->height())) {
+        // Dimensions must match
+        return false;
+    }
+
+    if (!reuseBitmap) {
+        bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+        // jpegs are always opaque (i.e. have no per-pixel alpha)
+        bm->setIsOpaque(true);
+
+        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+            return true;
+        }
+        if (!this->allocPixelRef(bm, NULL)) {
+            return return_false(cinfo, *bm, "allocPixelRef");
+        }
+    } else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
+    }
+
+    SkAutoLockPixels alp(*bm);                          
+    if (!sampler.begin(bm, sc, this->getDitherImage())) {
+        return return_false(cinfo, *bm, "sampler.begin");
+    }
+
+    uint8_t* srcRow = (uint8_t*)srcStorage.reset(cinfo.output_width * 4);
+
+    //  Possibly skip initial rows [sampler.srcY0]
+    if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
+        return return_false(cinfo, *bm, "skip rows");
+    }
+
+    // now loop through scanlines until y == bm->height() - 1
+    for (int y = 0;; y++) {
+        JSAMPLE* rowptr = (JSAMPLE*)srcRow;
+        int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+        if (0 == row_count) {
+            return return_false(cinfo, *bm, "read_scanlines");
+        }
+        if (this->shouldCancelDecode()) {
+            return return_false(cinfo, *bm, "shouldCancelDecode");
+        }
+        
+        sampler.next(srcRow);
+        if (bm->height() - 1 == y) {
+            // we're done
+            break;
+        }
+
+        if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
+            return return_false(cinfo, *bm, "skip rows");
+        }
+    }
+
+    // we formally skip the rest, so we don't get a complaint from libjpeg
+    if (!skip_src_rows(&cinfo, srcRow,
+                       cinfo.output_height - cinfo.output_scanline)) {
+        return return_false(cinfo, *bm, "skip rows");
+    }
+    if (reuseBitmap) {
+        bm->notifyPixelsChanged();
+    }
+    jpeg_finish_decompress(&cinfo);
+
+//    SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
+    return true;
+}
+
+bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream,
+                                        int *width, int *height) {
+    SkAutoMalloc  srcStorage;
+    SkJPEGImageIndex *index = new SkJPEGImageIndex;
+
+    jpeg_decompress_struct  *cinfo = (jpeg_decompress_struct*)
+                                        malloc(sizeof(jpeg_decompress_struct));
+    skjpeg_error_mgr        sk_err;
+    skjpeg_source_mgr       *sk_stream =
+        new skjpeg_source_mgr(stream, this, true);
+    if (cinfo == NULL || sk_stream == NULL) {
+        return false;
+    }
+
+    cinfo->err = jpeg_std_error(&sk_err);
+    sk_err.error_exit = skjpeg_error_exit;
+
+    // All objects need to be instantiated before this setjmp call so that
+    // they will be cleaned up properly if an error occurs.
+    if (setjmp(sk_err.fJmpBuf)) {
+        return false;
+    }
+
+    jpeg_create_decompress(cinfo);
+    cinfo->do_fancy_upsampling = 0;
+    cinfo->do_block_smoothing = 0;
+
+#ifdef SK_BUILD_FOR_ANDROID
+    overwrite_mem_buffer_size(cinfo);
+#endif
+
+    cinfo->src = sk_stream;
+    int status = jpeg_read_header(cinfo, true);
+    if (status != JPEG_HEADER_OK) {
+        return false;
+    }
+    index->index = (huffman_index*)malloc(sizeof(huffman_index));
+    jpeg_create_huffman_index(cinfo, index->index);
+
+    cinfo->scale_num = 1;
+    cinfo->scale_denom = 1;
+    if (!jpeg_build_huffman_index(cinfo, index->index)) {
+        return false;
+    }
+    if (fReporter)
+        fReporter->reportMemory(index->index->mem_used);
+    jpeg_destroy_decompress(cinfo);
+
+
+    // Init decoder to image decode mode
+    jpeg_create_decompress(cinfo);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    overwrite_mem_buffer_size(cinfo);
+#endif
+
+    cinfo->src = sk_stream;
+    status = jpeg_read_header(cinfo,true);
+    if (status != JPEG_HEADER_OK) {
+        return false;
+    }
+    cinfo->out_color_space = JCS_RGBA_8888;
+    cinfo->do_fancy_upsampling = 0;
+    cinfo->do_block_smoothing = 0;
+    //jpeg_start_decompress(cinfo);
+    jpeg_start_tile_decompress(cinfo);
+
+    cinfo->scale_num = 1;
+    index->cinfo = cinfo;
+    *height = cinfo->output_height;
+    *width = cinfo->output_width;
+    this->imageWidth = *width;
+    this->imageHeight = *height;
+    this->index = index;
+    return true;
+}
+
+bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) {
+    if (index == NULL) {
+        return false;
+    }
+    jpeg_decompress_struct *cinfo = index->cinfo;
+
+    SkIRect rect = SkIRect::MakeWH(this->imageWidth, this->imageHeight);
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, just
+        // returns false
+        return false;
+    }
+    SkAutoMalloc  srcStorage;
+    skjpeg_error_mgr        sk_err;
+    cinfo->err = jpeg_std_error(&sk_err);
+    sk_err.error_exit = skjpeg_error_exit;
+    if (setjmp(sk_err.fJmpBuf)) {
+        return false;
+    }
+    int requestedSampleSize = this->getSampleSize();
+    cinfo->scale_denom = requestedSampleSize;
+
+    if (this->getPreferQualityOverSpeed()) {
+        cinfo->dct_method = JDCT_ISLOW;
+    } else {
+        cinfo->dct_method = JDCT_IFAST;
+    }
+
+    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
+    if (config != SkBitmap::kARGB_8888_Config &&
+        config != SkBitmap::kARGB_4444_Config &&
+        config != SkBitmap::kRGB_565_Config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+
+    /* default format is RGB */
+    cinfo->out_color_space = JCS_RGB;
+
+#ifdef ANDROID_RGB
+    cinfo->dither_mode = JDITHER_NONE;
+    if (config == SkBitmap::kARGB_8888_Config) {
+        cinfo->out_color_space = JCS_RGBA_8888;
+    } else if (config == SkBitmap::kRGB_565_Config) {
+        cinfo->out_color_space = JCS_RGB_565;
+        if (this->getDitherImage()) {
+            cinfo->dither_mode = JDITHER_ORDERED;
+        }
+    }
+#endif
+    int startX = rect.fLeft;
+    int startY = rect.fTop;
+    int width = rect.width();
+    int height = rect.height();
+
+    jpeg_init_read_tile_scanline(cinfo, index->index,
+                                 &startX, &startY, &width, &height);
+    int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
+    int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
+
+    SkBitmap *bitmap = new SkBitmap;
+    SkAutoTDelete<SkBitmap> adb(bitmap);
+
+#ifdef ANDROID_RGB
+    /* short-circuit the SkScaledBitmapSampler when possible, as this gives
+       a significant performance boost.
+    */
+    if (skiaSampleSize == 1 &&
+        ((config == SkBitmap::kARGB_8888_Config &&
+                cinfo->out_color_space == JCS_RGBA_8888) ||
+        (config == SkBitmap::kRGB_565_Config &&
+                cinfo->out_color_space == JCS_RGB_565)))
+    {
+        bitmap->setConfig(config, cinfo->output_width, height);
+        bitmap->setIsOpaque(true);
+
+        // Check ahead of time if the swap(dest, src) is possible or not.
+        // If yes, then we will stick to AllocPixelRef since it's cheaper
+        // with the swap happening. If no, then we will use alloc to allocate
+        // pixels to prevent garbage collection.
+        //
+        // Not using a recycled-bitmap and the output rect is same as the
+        // decoded region.
+        int w = rect.width() / actualSampleSize;
+        int h = rect.height() / actualSampleSize;
+        bool swapOnly = (rect == region) && bm->isNull() &&
+                        (w == bitmap->width()) && (h == bitmap->height()) &&
+                        ((startX - rect.x()) / actualSampleSize == 0) &&
+                        ((startY - rect.y()) / actualSampleSize == 0);
+        if (swapOnly) {
+            if (!this->allocPixelRef(bitmap, NULL)) {
+                return return_false(*cinfo, *bitmap, "allocPixelRef");
+            }
+        } else {
+            if (!bitmap->allocPixels()) {
+                return return_false(*cinfo, *bitmap, "allocPixels");
+            }
+        }
+
+        SkAutoLockPixels alp(*bitmap);
+        JSAMPLE* rowptr = (JSAMPLE*)bitmap->getPixels();
+        INT32 const bpr = bitmap->rowBytes();
+        int row_total_count = 0;
+
+        while (row_total_count < height) {
+            int row_count = jpeg_read_tile_scanline(cinfo,
+                    index->index, &rowptr);
+            // if row_count == 0, then we didn't get a scanline, so abort.
+            // if we supported partial images, we might return true in this case
+            if (0 == row_count) {
+                return return_false(*cinfo, *bitmap, "read_scanlines");
+            }
+            if (this->shouldCancelDecode()) {
+                return return_false(*cinfo, *bitmap, "shouldCancelDecode");
+            }
+            row_total_count += row_count;
+            rowptr += bpr;
+        }
+
+        if (swapOnly) {
+            bm->swap(*bitmap);
+        } else {
+            cropBitmap(bm, bitmap, actualSampleSize, region.x(), region.y(),
+                       region.width(), region.height(), startX, startY);
+        }
+        return true;
+    }
+#endif
+    // check for supported formats
+    SkScaledBitmapSampler::SrcConfig sc;
+    if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
+        sc = SkScaledBitmapSampler::kRGB;
+#ifdef ANDROID_RGB
+    } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
+        sc = SkScaledBitmapSampler::kRGBX;
+    } else if (JCS_RGB_565 == cinfo->out_color_space) {
+        sc = SkScaledBitmapSampler::kRGB_565;
+#endif
+    } else if (1 == cinfo->out_color_components &&
+               JCS_GRAYSCALE == cinfo->out_color_space) {
+        sc = SkScaledBitmapSampler::kGray;
+    } else {
+        return return_false(*cinfo, *bm, "jpeg colorspace");
+    }
+
+    SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
+
+    bitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+    bitmap->setIsOpaque(true);
+
+    // Check ahead of time if the swap(dest, src) is possible or not.
+    // If yes, then we will stick to AllocPixelRef since it's cheaper with the
+    // swap happening. If no, then we will use alloc to allocate pixels to
+    // prevent garbage collection.
+    int w = rect.width() / actualSampleSize;
+    int h = rect.height() / actualSampleSize;
+    bool swapOnly = (rect == region) && bm->isNull() &&
+                    (w == bitmap->width()) && (h == bitmap->height()) &&
+                    ((startX - rect.x()) / actualSampleSize == 0) &&
+                    ((startY - rect.y()) / actualSampleSize == 0);
+    if (swapOnly) {
+        if (!this->allocPixelRef(bitmap, NULL)) {
+            return return_false(*cinfo, *bitmap, "allocPixelRef");
+        }
+    } else {
+        if (!bitmap->allocPixels()) {
+            return return_false(*cinfo, *bitmap, "allocPixels");
+        }
+    }
+
+    SkAutoLockPixels alp(*bitmap);
+    if (!sampler.begin(bitmap, sc, this->getDitherImage())) {
+        return return_false(*cinfo, *bitmap, "sampler.begin");
+    }
+
+    uint8_t* srcRow = (uint8_t*)srcStorage.reset(width * 4);
+
+    //  Possibly skip initial rows [sampler.srcY0]
+    if (!skip_src_rows_tile(cinfo, index->index, srcRow, sampler.srcY0())) {
+        return return_false(*cinfo, *bitmap, "skip rows");
+    }
+
+    // now loop through scanlines until y == bitmap->height() - 1
+    for (int y = 0;; y++) {
+        JSAMPLE* rowptr = (JSAMPLE*)srcRow;
+        int row_count = jpeg_read_tile_scanline(cinfo, index->index, &rowptr);
+        if (0 == row_count) {
+            return return_false(*cinfo, *bitmap, "read_scanlines");
+        }
+        if (this->shouldCancelDecode()) {
+            return return_false(*cinfo, *bitmap, "shouldCancelDecode");
+        }
+
+        sampler.next(srcRow);
+        if (bitmap->height() - 1 == y) {
+            // we're done
+            break;
+        }
+
+        if (!skip_src_rows_tile(cinfo, index->index, srcRow,
+                                sampler.srcDY() - 1)) {
+            return return_false(*cinfo, *bitmap, "skip rows");
+        }
+    }
+    if (swapOnly) {
+        bm->swap(*bitmap);
+    } else {
+        cropBitmap(bm, bitmap, actualSampleSize, region.x(), region.y(),
+                   region.width(), region.height(), startX, startY);
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+
+// taken from jcolor.c in libjpeg
+#if 0   // 16bit - precise but slow
+    #define CYR     19595   // 0.299
+    #define CYG     38470   // 0.587
+    #define CYB      7471   // 0.114
+
+    #define CUR    -11059   // -0.16874
+    #define CUG    -21709   // -0.33126
+    #define CUB     32768   // 0.5
+
+    #define CVR     32768   // 0.5
+    #define CVG    -27439   // -0.41869
+    #define CVB     -5329   // -0.08131
+
+    #define CSHIFT  16
+#else      // 8bit - fast, slightly less precise
+    #define CYR     77    // 0.299
+    #define CYG     150    // 0.587
+    #define CYB      29    // 0.114
+
+    #define CUR     -43    // -0.16874
+    #define CUG    -85    // -0.33126
+    #define CUB     128    // 0.5
+
+    #define CVR      128   // 0.5
+    #define CVG     -107   // -0.41869
+    #define CVB      -21   // -0.08131
+
+    #define CSHIFT  8
+#endif
+
+static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
+    int r = SkGetPackedR32(c);
+    int g = SkGetPackedG32(c);
+    int b = SkGetPackedB32(c);
+
+    int  y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
+    int  u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
+    int  v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
+
+    dst[0] = SkToU8(y);
+    dst[1] = SkToU8(u + 128);
+    dst[2] = SkToU8(v + 128);
+}
+
+static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
+    int r = SkGetPackedR4444(c);
+    int g = SkGetPackedG4444(c);
+    int b = SkGetPackedB4444(c);
+
+    int  y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
+    int  u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
+    int  v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
+
+    dst[0] = SkToU8(y);
+    dst[1] = SkToU8(u + 128);
+    dst[2] = SkToU8(v + 128);
+}
+
+static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
+    int r = SkGetPackedR16(c);
+    int g = SkGetPackedG16(c);
+    int b = SkGetPackedB16(c);
+
+    int  y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
+    int  u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
+    int  v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
+
+    dst[0] = SkToU8(y);
+    dst[1] = SkToU8(u + 128);
+    dst[2] = SkToU8(v + 128);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
+                              const void* SK_RESTRICT src, int width,
+                              const SkPMColor* SK_RESTRICT ctable);
+
+static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
+                         const void* SK_RESTRICT srcRow, int width,
+                         const SkPMColor*) {
+    const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_32(dst, *src++);
+#else
+        uint32_t c = *src++;
+        dst[0] = SkGetPackedR32(c);
+        dst[1] = SkGetPackedG32(c);
+        dst[2] = SkGetPackedB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
+                           const void* SK_RESTRICT srcRow, int width,
+                           const SkPMColor*) {
+    const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_4444(dst, *src++);
+#else
+        SkPMColor16 c = *src++;
+        dst[0] = SkPacked4444ToR32(c);
+        dst[1] = SkPacked4444ToG32(c);
+        dst[2] = SkPacked4444ToB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
+                         const void* SK_RESTRICT srcRow, int width,
+                         const SkPMColor*) {
+    const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_16(dst, *src++);
+#else
+        uint16_t c = *src++;
+        dst[0] = SkPacked16ToR32(c);
+        dst[1] = SkPacked16ToG32(c);
+        dst[2] = SkPacked16ToB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
+                            const void* SK_RESTRICT srcRow, int width,
+                            const SkPMColor* SK_RESTRICT ctable) {
+    const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_32(dst, ctable[*src++]);
+#else
+        uint32_t c = ctable[*src++];
+        dst[0] = SkGetPackedR32(c);
+        dst[1] = SkGetPackedG32(c);
+        dst[2] = SkGetPackedB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static WriteScanline ChooseWriter(const SkBitmap& bm) {
+    switch (bm.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            return Write_32_YUV;
+        case SkBitmap::kRGB_565_Config:
+            return Write_16_YUV;
+        case SkBitmap::kARGB_4444_Config:
+            return Write_4444_YUV;
+        case SkBitmap::kIndex8_Config:
+            return Write_Index_YUV;
+        default:
+            return NULL;
+    }
+}
+
+class SkJPEGImageEncoder : public SkImageEncoder {
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
+#ifdef TIME_ENCODE
+        AutoTimeMillis atm("JPEG Encode");
+#endif
+
+        const WriteScanline writer = ChooseWriter(bm);
+        if (NULL == writer) {
+            return false;
+        }
+
+        SkAutoLockPixels alp(bm);
+        if (NULL == bm.getPixels()) {
+            return false;
+        }
+
+        jpeg_compress_struct    cinfo;
+        skjpeg_error_mgr        sk_err;
+        skjpeg_destination_mgr  sk_wstream(stream);
+
+        // allocate these before set call setjmp
+        SkAutoMalloc    oneRow;
+        SkAutoLockColors ctLocker;
+
+        cinfo.err = jpeg_std_error(&sk_err);
+        sk_err.error_exit = skjpeg_error_exit;
+        if (setjmp(sk_err.fJmpBuf)) {
+            return false;
+        }
+        jpeg_create_compress(&cinfo);
+
+        cinfo.dest = &sk_wstream;
+        cinfo.image_width = bm.width();
+        cinfo.image_height = bm.height();
+        cinfo.input_components = 3;
+#ifdef WE_CONVERT_TO_YUV
+        cinfo.in_color_space = JCS_YCbCr;
+#else
+        cinfo.in_color_space = JCS_RGB;
+#endif
+        cinfo.input_gamma = 1;
+
+        jpeg_set_defaults(&cinfo);
+        jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
+        cinfo.dct_method = JDCT_IFAST;
+
+        jpeg_start_compress(&cinfo, TRUE);
+
+        const int       width = bm.width();
+        uint8_t*        oneRowP = (uint8_t*)oneRow.reset(width * 3);
+
+        const SkPMColor* colors = ctLocker.lockColors(bm);
+        const void*      srcRow = bm.getPixels();
+
+        while (cinfo.next_scanline < cinfo.image_height) {
+            JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
+
+            writer(oneRowP, srcRow, width, colors);
+            row_pointer[0] = oneRowP;
+            (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+            srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
+        }
+
+        jpeg_finish_compress(&cinfo);
+        jpeg_destroy_compress(&cinfo);
+
+        return true;
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* DFactory(SkStream* stream) {
+    static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
+    static const size_t HEADER_SIZE = sizeof(gHeader);
+
+    char buffer[HEADER_SIZE];
+    size_t len = stream->read(buffer, HEADER_SIZE);
+
+    if (len != HEADER_SIZE) {
+        return NULL;   // can't read enough
+    }
+    if (memcmp(buffer, gHeader, HEADER_SIZE)) {
+        return NULL;
+    }
+    return SkNEW(SkJPEGImageDecoder);
+}
+
+static SkImageEncoder* 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);
diff --git a/legacy/src/images/SkImageDecoder_libpng.cpp b/legacy/src/images/SkImageDecoder_libpng.cpp
new file mode 100644
index 0000000..fa35239
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_libpng.cpp
@@ -0,0 +1,1193 @@
+
+/*
+ * 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 "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkMath.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+extern "C" {
+#include "png.h"
+}
+
+class SkPNGImageIndex {
+public:
+    SkPNGImageIndex() {
+        inputStream = NULL;
+        png_ptr = NULL;
+    }
+    virtual ~SkPNGImageIndex() {
+        if (png_ptr) {
+            png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+        }
+        if (inputStream) {
+            delete inputStream;
+        }
+    }
+    png_structp png_ptr;
+    png_infop info_ptr;
+    SkStream *inputStream;
+};
+
+class SkPNGImageDecoder : public SkImageDecoder {
+public:
+    SkPNGImageDecoder() {
+        index = NULL;
+    }
+    virtual Format getFormat() const {
+        return kPNG_Format;
+    }
+    virtual ~SkPNGImageDecoder() {
+        if (index) {
+            delete index;
+        }
+    }
+
+protected:
+    virtual bool onBuildTileIndex(SkStream *stream,
+             int *width, int *height);
+    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect region);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+
+private:
+    bool onDecodeInit(SkStream* stream, png_structp *png_ptrp,
+            png_infop *info_ptrp);
+    bool decodePalette(png_structp png_ptr, png_infop info_ptr,
+        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep);
+    bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
+        SkBitmap::Config *config, bool *hasAlpha, bool *doDither,
+        SkPMColor *theTranspColor);
+    SkPNGImageIndex *index;
+};
+
+#ifndef png_jmpbuf
+#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#endif
+
+#define PNG_BYTES_TO_CHECK 4
+
+/* Automatically clean up after throwing an exception */
+struct PNGAutoClean {
+    PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
+    ~PNGAutoClean() {
+        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+    }
+private:
+    png_structp png_ptr;
+    png_infop info_ptr;
+};
+
+static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
+    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
+    size_t bytes = sk_stream->read(data, length);
+    if (bytes != length) {
+        png_error(png_ptr, "Read Error!");
+    }
+}
+
+static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
+    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
+    sk_stream->rewind();
+    (void)sk_stream->skip(offset);
+}
+
+static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
+    SkImageDecoder::Peeker* peeker =
+                    (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
+    // peek() returning true means continue decoding
+    return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
+            1 : -1;
+}
+
+static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
+#if 0
+    SkDebugf("------ png error %s\n", msg);
+#endif
+    longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
+    for (int i = 0; i < count; i++) {
+        uint8_t* tmp = storage;
+        png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+    }
+}
+
+static bool pos_le(int value, int max) {
+    return value > 0 && value <= max;
+}
+
+static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
+    SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
+    
+    bool reallyHasAlpha = false;
+
+    for (int y = bm->height() - 1; y >= 0; --y) {
+        SkPMColor* p = bm->getAddr32(0, y);
+        for (int x = bm->width() - 1; x >= 0; --x) {
+            if (match == *p) {
+                *p = 0;
+                reallyHasAlpha = true;
+            }
+            p += 1;
+        }
+    }
+    return reallyHasAlpha;
+}
+
+static bool canUpscalePaletteToConfig(SkBitmap::Config dstConfig,
+                                      bool srcHasAlpha) {
+    switch (dstConfig) {
+        case SkBitmap::kARGB_8888_Config:
+        case SkBitmap::kARGB_4444_Config:
+            return true;
+        case SkBitmap::kRGB_565_Config:
+            // only return true if the src is opaque (since 565 is opaque)
+            return !srcHasAlpha;
+        default:
+            return false;
+    }
+}
+
+// call only if color_type is PALETTE. Returns true if the ctable has alpha
+static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
+    png_bytep trans;
+    int num_trans;
+
+    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+        png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
+        return num_trans > 0;
+    }
+    return false;
+}
+
+bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream,
+        png_structp *png_ptrp, png_infop *info_ptrp)
+{
+    /* Create and initialize the png_struct with the desired error handler
+    * functions.  If you want to use the default stderr and longjump method,
+    * you can supply NULL for the last three parameters.  We also supply the
+    * the compiler header file version, so that we know if the application
+    * was compiled with a compatible version of the library.  */
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+        NULL, sk_error_fn, NULL);
+    //   png_voidp user_error_ptr, user_error_fn, user_warning_fn);
+    if (png_ptr == NULL) {
+        return false;
+    }
+    *png_ptrp = png_ptr;
+
+    /* Allocate/initialize the memory for image information. */
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (info_ptr == NULL) {
+        png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
+        return false;
+    }
+    *info_ptrp = info_ptr;
+
+    /* Set error handling if you are using the setjmp/longjmp method (this is
+    * the normal method of doing things with libpng).  REQUIRED unless you
+    * set up your own error handlers in the png_create_read_struct() earlier.
+    */
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
+
+    /* If you are using replacement read functions, instead of calling
+    * png_init_io() here you would call:
+    */
+    png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
+    png_set_seek_fn(png_ptr, sk_seek_fn);
+    /* where user_io_ptr is a structure you want available to the callbacks */
+    /* If we have already read some of the signature */
+    // png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
+
+    // hookup our peeker so we can see any user-chunks the caller may be interested in
+    png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
+    if (this->getPeeker()) {
+        png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
+    }
+
+    /* The call to png_read_info() gives us all of the information from the
+    * PNG file before the first IDAT (image data chunk). */
+    png_read_info(png_ptr, info_ptr);
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    /* tell libpng to strip 16 bit/color files down to 8 bits/color */
+    if (bit_depth == 16) {
+        png_set_strip_16(png_ptr);
+    }
+    /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
+     * byte into separate bytes (useful for paletted and grayscale images). */
+    if (bit_depth < 8) {
+        png_set_packing(png_ptr);
+    }
+    /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+        png_set_gray_1_2_4_to_8(png_ptr);
+    }
+
+    /* Make a grayscale image into RGB. */
+    if (color_type == PNG_COLOR_TYPE_GRAY ||
+        color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        png_set_gray_to_rgb(png_ptr);
+    }
+    return true;
+}
+
+bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
+                                 Mode mode) {
+    png_structp png_ptr;
+    png_infop info_ptr;
+
+    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
+        return false;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
+
+    PNGAutoClean autoClean(png_ptr, info_ptr);
+
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    SkBitmap::Config    config;
+    bool                hasAlpha = false;
+    bool                doDither = this->getDitherImage();
+    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
+
+    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
+                &doDither, &theTranspColor) == false) {
+        return false;
+    }
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+    decodedBitmap->lockPixels();
+    void* rowptr = (void*) decodedBitmap->getPixels();
+    bool reuseBitmap = (rowptr != NULL);
+    decodedBitmap->unlockPixels();
+    if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() ||
+            sampler.scaledHeight() != decodedBitmap->height())) {
+        // Dimensions must match
+        return false;
+    }
+
+    if (!reuseBitmap) {
+        decodedBitmap->setConfig(config, sampler.scaledWidth(),
+                                 sampler.scaledHeight(), 0);
+    }
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
+    }
+
+    // from here down we are concerned with colortables and pixels
+
+    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
+    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
+    // draw lots faster if we can flag the bitmap has being opaque
+    bool reallyHasAlpha = false;
+    SkColorTable* colorTable = NULL;
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        decodePalette(png_ptr, info_ptr, &hasAlpha,
+                &reallyHasAlpha, &colorTable);
+    }
+
+    SkAutoUnref aur(colorTable);
+
+    if (!reuseBitmap) {
+        if (!this->allocPixelRef(decodedBitmap,
+                                 SkBitmap::kIndex8_Config == config ?
+                                    colorTable : NULL)) {
+            return false;
+        }
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    /* Add filler (or alpha) byte (before/after each RGB triplet) */
+    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
+        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+    }
+
+    /* Turn on interlace handling.  REQUIRED if you are not using
+    * png_read_image().  To see how to handle interlacing passes,
+    * see the png_read_row() method below:
+    */
+    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
+                        png_set_interlace_handling(png_ptr) : 1;
+
+    /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (ie you selected such a transform above).
+    */
+    png_read_update_info(png_ptr, info_ptr);
+
+    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+        for (int i = 0; i < number_passes; i++) {
+            for (png_uint_32 y = 0; y < origHeight; y++) {
+                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+        }
+    } else {
+        SkScaledBitmapSampler::SrcConfig sc;
+        int srcBytesPerPixel = 4;
+
+        if (colorTable != NULL) {
+            sc = SkScaledBitmapSampler::kIndex;
+            srcBytesPerPixel = 1;
+        } else if (hasAlpha) {
+            sc = SkScaledBitmapSampler::kRGBA;
+        } else {
+            sc = SkScaledBitmapSampler::kRGBX;
+        }
+
+        /*  We have to pass the colortable explicitly, since we may have one
+            even if our decodedBitmap doesn't, due to the request that we
+            upscale png's palette to a direct model
+         */
+        SkAutoLockColors ctLock(colorTable);
+        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
+            return false;
+        }
+        const int height = decodedBitmap->height();
+
+        if (number_passes > 1) {
+            SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
+            uint8_t* base = (uint8_t*)storage.get();
+            size_t rb = origWidth * srcBytesPerPixel;
+
+            for (int i = 0; i < number_passes; i++) {
+                uint8_t* row = base;
+                for (png_uint_32 y = 0; y < origHeight; y++) {
+                    uint8_t* bmRow = row;
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+                    row += rb;
+                }
+            }
+            // now sample it
+            base += sampler.srcY0() * rb;
+            for (int y = 0; y < height; y++) {
+                reallyHasAlpha |= sampler.next(base);
+                base += sampler.srcDY() * rb;
+            }
+        } else {
+            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
+            uint8_t* srcRow = (uint8_t*)storage.get();
+            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
+
+            for (int y = 0; y < height; y++) {
+                uint8_t* tmp = srcRow;
+                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+                reallyHasAlpha |= sampler.next(srcRow);
+                if (y < height - 1) {
+                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
+                }
+            }
+
+            // skip the rest of the rows (if any)
+            png_uint_32 read = (height - 1) * sampler.srcDY() +
+                               sampler.srcY0() + 1;
+            SkASSERT(read <= origHeight);
+            skip_src_rows(png_ptr, srcRow, origHeight - read);
+        }
+    }
+
+    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+    png_read_end(png_ptr, info_ptr);
+
+    if (0 != theTranspColor) {
+        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+    }
+    decodedBitmap->setIsOpaque(!reallyHasAlpha);
+    if (reuseBitmap) {
+        decodedBitmap->notifyPixelsChanged();
+    }
+    return true;
+}
+
+bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width,
+        int *height) {
+    png_structp png_ptr;
+    png_infop   info_ptr;
+
+    this->index = new SkPNGImageIndex();
+
+    if (onDecodeInit(sk_stream, &png_ptr, &info_ptr) == false) {
+        return false;
+    }
+
+    int bit_depth, color_type, interlace_type;
+    png_uint_32 origWidth, origHeight;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    *width = origWidth;
+    *height = origHeight;
+
+    png_build_index(png_ptr);
+    this->index->png_ptr = png_ptr;
+    this->index->info_ptr = info_ptr;
+    return true;
+}
+
+bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
+        SkBitmap::Config *configp, bool *hasAlphap, bool *doDitherp,
+        SkPMColor *theTranspColorp) {
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    // check for sBIT chunk data, in case we should disable dithering because
+    // our data is not truely 8bits per component
+    if (*doDitherp) {
+#if 0
+        SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
+                 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
+                 info_ptr->sig_bit.alpha);
+#endif
+        // 0 seems to indicate no information available
+        if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
+                pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
+                pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
+            *doDitherp = false;
+        }
+    }
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
+        *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
+        // now see if we can upscale to their requested config
+        if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
+            *configp = SkBitmap::kIndex8_Config;
+        }
+    } else {
+        png_color_16p   transpColor = NULL;
+        int             numTransp = 0;
+
+        png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
+
+        bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
+
+        if (valid && numTransp == 1 && transpColor != NULL) {
+            /*  Compute our transparent color, which we'll match against later.
+                We don't really handle 16bit components properly here, since we
+                do our compare *after* the values have been knocked down to 8bit
+                which means we will find more matches than we should. The real
+                fix seems to be to see the actual 16bit components, do the
+                compare, and then knock it down to 8bits ourselves.
+            */
+            if (color_type & PNG_COLOR_MASK_COLOR) {
+                if (16 == bit_depth) {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
+                              transpColor->green >> 8, transpColor->blue >> 8);
+                } else {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
+                                      transpColor->green, transpColor->blue);
+                }
+            } else {    // gray
+                if (16 == bit_depth) {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
+                              transpColor->gray >> 8, transpColor->gray >> 8);
+                } else {
+                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
+                                          transpColor->gray, transpColor->gray);
+                }
+            }
+        }
+
+        if (valid ||
+                PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
+                PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
+            *hasAlphap = true;
+        }
+        *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
+        // now match the request against our capabilities
+        if (*hasAlphap) {
+            if (*configp != SkBitmap::kARGB_4444_Config) {
+                *configp = SkBitmap::kARGB_8888_Config;
+            }
+        } else {
+            if (*configp != SkBitmap::kRGB_565_Config &&
+                *configp != SkBitmap::kARGB_4444_Config) {
+                *configp = SkBitmap::kARGB_8888_Config;
+            }
+        }
+    }
+
+    // sanity check for size
+    {
+        Sk64 size;
+        size.setMul(origWidth, origHeight);
+        if (size.isNeg() || !size.is32()) {
+            return false;
+        }
+        // now check that if we are 4-bytes per pixel, we also don't overflow
+        if (size.get32() > (0x7FFFFFFF >> 2)) {
+            return false;
+        }
+    }
+
+    if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
+        return false;
+    }
+    return true;
+}
+
+bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
+        bool *hasAlphap, bool *reallyHasAlphap, SkColorTable **colorTablep) {
+    int num_palette;
+    png_colorp palette;
+    png_bytep trans;
+    int num_trans;
+    bool reallyHasAlpha = false;
+    SkColorTable* colorTable = NULL;
+
+    png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+
+    /*  BUGGY IMAGE WORKAROUND
+
+        We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
+        which is a problem since we use the byte as an index. To work around this we grow
+        the colortable by 1 (if its < 256) and duplicate the last color into that slot.
+        */
+    int colorCount = num_palette + (num_palette < 256);
+
+    colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
+
+    SkPMColor* colorPtr = colorTable->lockColors();
+    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+        png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
+        *hasAlphap = (num_trans > 0);
+    } else {
+        num_trans = 0;
+        colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+    }
+    // check for bad images that might make us crash
+    if (num_trans > num_palette) {
+        num_trans = num_palette;
+    }
+
+    int index = 0;
+    int transLessThanFF = 0;
+
+    for (; index < num_trans; index++) {
+        transLessThanFF |= (int)*trans - 0xFF;
+        *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
+        palette++;
+    }
+    reallyHasAlpha |= (transLessThanFF < 0);
+
+    for (; index < num_palette; index++) {
+        *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
+        palette++;
+    }
+
+    // see BUGGY IMAGE WORKAROUND comment above
+    if (num_palette < 256) {
+        *colorPtr = colorPtr[-1];
+    }
+    colorTable->unlockColors(true);
+    *colorTablep = colorTable;
+    *reallyHasAlphap = reallyHasAlpha;
+    return true;
+}
+
+bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) {
+    int i;
+    png_structp png_ptr = this->index->png_ptr;
+    png_infop info_ptr = this->index->info_ptr;
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
+
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
+            &color_type, &interlace_type, int_p_NULL, int_p_NULL);
+
+    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
+
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, just
+        // returns false
+        return false;
+    }
+
+    SkBitmap::Config    config;
+    bool                hasAlpha = false;
+    bool                doDither = this->getDitherImage();
+    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
+
+    if (getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha,
+                &doDither, &theTranspColor) == false) {
+        return false;
+    }
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
+
+    SkBitmap *decodedBitmap = new SkBitmap;
+    SkAutoTDelete<SkBitmap> adb(decodedBitmap);
+
+    decodedBitmap->setConfig(config, sampler.scaledWidth(),
+                             sampler.scaledHeight(), 0);
+
+    // from here down we are concerned with colortables and pixels
+
+    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
+    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
+    // draw lots faster if we can flag the bitmap has being opaque
+    bool reallyHasAlpha = false;
+    SkColorTable* colorTable = NULL;
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        decodePalette(png_ptr, info_ptr, &hasAlpha,
+                &reallyHasAlpha, &colorTable);
+    }
+
+    SkAutoUnref aur(colorTable);
+
+    // Check ahead of time if the swap(dest, src) is possible in crop or not.
+    // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
+    // If no, then we will use alloc to allocate pixels to prevent garbage collection.
+    int w = rect.width() / sampleSize;
+    int h = rect.height() / sampleSize;
+    bool swapOnly = (rect == region) && (w == decodedBitmap->width()) &&
+                    (h == decodedBitmap->height()) &&
+                    ((0 - rect.x()) / sampleSize == 0) && bm->isNull();
+    if (swapOnly) {
+        if (!this->allocPixelRef(decodedBitmap,
+                SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
+            return false;
+        }
+    } else {
+        if (!decodedBitmap->allocPixels(
+            NULL, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
+            return false;
+        }
+    }
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    /* Add filler (or alpha) byte (before/after each RGB triplet) */
+    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
+        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+    }
+
+    /* Turn on interlace handling.  REQUIRED if you are not using
+    * png_read_image().  To see how to handle interlacing passes,
+    * see the png_read_row() method below:
+    */
+    const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
+                        png_set_interlace_handling(png_ptr) : 1;
+
+    /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (ie you selected such a transform above).
+    */
+    png_ptr->pass = 0;
+    png_read_update_info(png_ptr, info_ptr);
+
+    int actualTop = rect.fTop;
+
+    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+        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);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+            for (png_uint_32 y = 0; y < origHeight; y++) {
+                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+        }
+    } else {
+        SkScaledBitmapSampler::SrcConfig sc;
+        int srcBytesPerPixel = 4;
+
+        if (colorTable != NULL) {
+            sc = SkScaledBitmapSampler::kIndex;
+            srcBytesPerPixel = 1;
+        } else if (hasAlpha) {
+            sc = SkScaledBitmapSampler::kRGBA;
+        } else {
+            sc = SkScaledBitmapSampler::kRGBX;
+        }
+
+        /*  We have to pass the colortable explicitly, since we may have one
+            even if our decodedBitmap doesn't, due to the request that we
+            upscale png's palette to a direct model
+         */
+        SkAutoLockColors ctLock(colorTable);
+        if (!sampler.begin(decodedBitmap, sc, doDither, ctLock.colors())) {
+            return false;
+        }
+        const int height = decodedBitmap->height();
+
+        if (number_passes > 1) {
+            SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
+            uint8_t* base = (uint8_t*)storage.get();
+            size_t rb = origWidth * srcBytesPerPixel;
+
+            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);
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+                }
+                uint8_t* row = base;
+                for (png_uint_32 y = 0; y < rect.height(); y++) {
+                    uint8_t* bmRow = row;
+                    png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+                    row += rb;
+                }
+            }
+            // now sample it
+            base += sampler.srcY0() * rb;
+            for (int y = 0; y < height; y++) {
+                reallyHasAlpha |= sampler.next(base);
+                base += sampler.srcDY() * rb;
+            }
+        } else {
+            SkAutoMalloc storage(origWidth * srcBytesPerPixel);
+            uint8_t* srcRow = (uint8_t*)storage.get();
+
+            png_configure_decoder(png_ptr, &actualTop, 0);
+            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
+
+            for (int i = 0; i < rect.fTop - actualTop; i++) {
+                uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+            for (int y = 0; y < height; y++) {
+                uint8_t* tmp = srcRow;
+                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+                reallyHasAlpha |= sampler.next(srcRow);
+                if (y < height - 1) {
+                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
+                }
+            }
+        }
+    }
+    if (swapOnly) {
+        bm->swap(*decodedBitmap);
+    } else {
+        cropBitmap(bm, decodedBitmap, sampleSize, region.x(), region.y(),
+                   region.width(), region.height(), 0, rect.y());
+    }
+
+    if (0 != theTranspColor) {
+        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+    }
+    decodedBitmap->setIsOpaque(!reallyHasAlpha);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+#include "SkUnPreMultiply.h"
+
+static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
+    SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
+    if (!sk_stream->write(data, len)) {
+        png_error(png_ptr, "sk_write_fn Error!");
+    }
+}
+
+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
+    // colortable packing cares about that distinction, not the pixels
+    if (SkBitmap::kIndex8_Config == config) {
+        hasAlpha = false;   // we store false in the table entries for kIndex8
+    }
+    
+    static const struct {
+        SkBitmap::Config        fConfig;
+        bool                    fHasAlpha;
+        transform_scanline_proc fProc;
+    } gMap[] = {
+        { SkBitmap::kRGB_565_Config,    false,  transform_scanline_565 },
+        { SkBitmap::kARGB_8888_Config,  false,  transform_scanline_888 },
+        { 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 },
+    };
+
+    for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
+        if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
+            return gMap[i].fProc;
+        }
+    }
+    sk_throw();
+    return NULL;
+}
+
+// return the minimum legal bitdepth (by png standards) for this many colortable
+// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
+// we can use fewer bits per in png
+static int computeBitDepth(int colorCount) {
+#if 0
+    int bits = SkNextLog2(colorCount);
+    SkASSERT(bits >= 1 && bits <= 8);
+    // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
+    return SkNextPow2(bits);
+#else
+    // for the moment, we don't know how to pack bitdepth < 8
+    return 8;
+#endif
+}
+
+/*  Pack palette[] with the corresponding colors, and if hasAlpha is true, also
+    pack trans[] and return the number of trans[] entries written. If hasAlpha
+    is false, the return value will always be 0.
+ 
+    Note: this routine takes care of unpremultiplying the RGB values when we
+    have alpha in the colortable, since png doesn't support premul colors
+*/
+static inline int pack_palette(SkColorTable* ctable,
+                               png_color* SK_RESTRICT palette,
+                               png_byte* SK_RESTRICT trans, bool hasAlpha) {
+    SkAutoLockColors alc(ctable);
+    const SkPMColor* SK_RESTRICT colors = alc.colors();
+    const int ctCount = ctable->count();
+    int i, num_trans = 0;
+
+    if (hasAlpha) {
+        /*  first see if we have some number of fully opaque at the end of the
+            ctable. PNG allows num_trans < num_palette, but all of the trans
+            entries must come first in the palette. If I was smarter, I'd
+            reorder the indices and ctable so that all non-opaque colors came
+            first in the palette. But, since that would slow down the encode,
+            I'm leaving the indices and ctable order as is, and just looking
+            at the tail of the ctable for opaqueness.
+        */
+        num_trans = ctCount;
+        for (i = ctCount - 1; i >= 0; --i) {
+            if (SkGetPackedA32(colors[i]) != 0xFF) {
+                break;
+            }
+            num_trans -= 1;
+        }
+        
+        const SkUnPreMultiply::Scale* SK_RESTRICT table =
+                                            SkUnPreMultiply::GetScaleTable();
+
+        for (i = 0; i < num_trans; i++) {
+            const SkPMColor c = *colors++;
+            const unsigned a = SkGetPackedA32(c);
+            const SkUnPreMultiply::Scale s = table[a];
+            trans[i] = a;
+            palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
+            palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
+            palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
+        }        
+        // now fall out of this if-block to use common code for the trailing
+        // opaque entries
+    }
+    
+    // these (remaining) entries are opaque
+    for (i = num_trans; i < ctCount; i++) {
+        SkPMColor c = *colors++;
+        palette[i].red = SkGetPackedR32(c);
+        palette[i].green = SkGetPackedG32(c);
+        palette[i].blue = SkGetPackedB32(c);
+    }
+    return num_trans;
+}
+
+class SkPNGImageEncoder : public SkImageEncoder {
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+private:
+    bool doEncode(SkWStream* stream, const SkBitmap& bm,
+                  const bool& hasAlpha, int colorType,
+                  int bitDepth, SkBitmap::Config config,
+                  png_color_8& sig_bit);
+};
+
+bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
+                                 int /*quality*/) {
+    SkBitmap::Config config = bitmap.getConfig();
+
+    const bool hasAlpha = !bitmap.isOpaque();
+    int colorType = PNG_COLOR_MASK_COLOR;
+    int bitDepth = 8;   // default for color
+    png_color_8 sig_bit;
+
+    switch (config) {
+        case SkBitmap::kIndex8_Config:
+            colorType |= PNG_COLOR_MASK_PALETTE;
+            // fall through to the ARGB_8888 case
+        case SkBitmap::kARGB_8888_Config:
+            sig_bit.red = 8;
+            sig_bit.green = 8;
+            sig_bit.blue = 8;
+            sig_bit.alpha = 8;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            sig_bit.red = 4;
+            sig_bit.green = 4;
+            sig_bit.blue = 4;
+            sig_bit.alpha = 4;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            sig_bit.red = 5;
+            sig_bit.green = 6;
+            sig_bit.blue = 5;
+            sig_bit.alpha = 0;
+            break;
+        default:
+            return false;
+    }
+    
+    if (hasAlpha) {
+        // don't specify alpha if we're a palette, even if our ctable has alpha
+        if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
+            colorType |= PNG_COLOR_MASK_ALPHA;
+        }
+    } else {
+        sig_bit.alpha = 0;
+    }
+    
+    SkAutoLockPixels alp(bitmap);
+    // readyToDraw checks for pixels (and colortable if that is required)
+    if (!bitmap.readyToDraw()) {
+        return false;
+    }
+
+    // we must do this after we have locked the pixels
+    SkColorTable* ctable = bitmap.getColorTable();
+    if (NULL != ctable) {
+        if (ctable->count() == 0) {
+            return false;
+        }
+        // check if we can store in fewer than 8 bits
+        bitDepth = computeBitDepth(ctable->count());
+    }
+
+    return doEncode(stream, bitmap, hasAlpha, colorType,
+                    bitDepth, config, sig_bit);
+}
+
+bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
+                  const bool& hasAlpha, int colorType,
+                  int bitDepth, SkBitmap::Config config,
+                  png_color_8& sig_bit) {
+
+    png_structp png_ptr;
+    png_infop info_ptr;
+
+    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
+                                      NULL);
+    if (NULL == png_ptr) {
+        return false;
+    }
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (NULL == info_ptr) {
+        png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
+        return false;
+    }
+
+    /* Set error handling.  REQUIRED if you aren't supplying your own
+    * error handling functions in the png_create_write_struct() call.
+    */
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return false;
+    }
+
+    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
+
+    /* Set the image information here.  Width and height are up to 2^31,
+    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
+    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
+    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
+    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
+    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
+    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
+    */
+
+    png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
+                 bitDepth, colorType,
+                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+                 PNG_FILTER_TYPE_BASE);
+
+    // set our colortable/trans arrays if needed
+    png_color paletteColors[256];
+    png_byte trans[256];
+    if (SkBitmap::kIndex8_Config == config) {
+        SkColorTable* ct = bitmap.getColorTable();
+        int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
+        png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
+        if (numTrans > 0) {
+            png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
+        }
+    }
+
+    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+    png_write_info(png_ptr, info_ptr);
+
+    const char* srcImage = (const char*)bitmap.getPixels();
+    SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
+    char* storage = (char*)rowStorage.get();
+    transform_scanline_proc proc = choose_proc(config, hasAlpha);
+
+    for (int y = 0; y < bitmap.height(); y++) {
+        png_bytep row_ptr = (png_bytep)storage;
+        proc(srcImage, bitmap.width(), storage);
+        png_write_rows(png_ptr, &row_ptr, 1);
+        srcImage += bitmap.rowBytes();
+    }
+
+    png_write_end(png_ptr, info_ptr);
+
+    /* clean up after the write, and free any memory allocated */
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+#ifdef SK_ENABLE_LIBPNG
+    SkImageDecoder* sk_libpng_dfactory(SkStream*);
+    SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type);
+#endif
+
+SkImageDecoder* sk_libpng_dfactory(SkStream* stream) {
+    char buf[PNG_BYTES_TO_CHECK];
+    if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
+        !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
+        return SkNEW(SkPNGImageDecoder);
+    }
+    return NULL;
+}
+
+SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
+    return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
+}
+
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libpng_efactory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libpng_dfactory);
diff --git a/legacy/src/images/SkImageDecoder_libwebp.cpp b/legacy/src/images/SkImageDecoder_libwebp.cpp
new file mode 100644
index 0000000..3ca1254
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_libwebp.cpp
@@ -0,0 +1,589 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkTScopedPtr.h"
+
+// A WebP decoder only, on top of (subset of) libwebp
+// For more information on WebP image format, and libwebp library, see:
+//   http://code.google.com/speed/webp/
+//   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
+//   http://review.webmproject.org/gitweb?p=libwebp.git
+
+#include <stdio.h>
+extern "C" {
+// If moving libwebp out of skia source tree, path for webp headers must be
+// updated accordingly. Here, we enforce using local copy in webp sub-directory.
+#include "webp/decode.h"
+#include "webp/encode.h"
+}
+
+#ifdef ANDROID
+#include <cutils/properties.h>
+
+// Key to lookup the size of memory buffer set in system property
+static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap";
+#endif
+
+// this enables timing code to report milliseconds for a decode
+//#define TIME_DECODE
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+// Define VP8 I/O on top of Skia stream
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+static const size_t WEBP_VP8_HEADER_SIZE = 64;
+static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
+
+// Parse headers of RIFF container, and check for valid Webp (VP8) content.
+static bool webp_parse_header(SkStream* stream, int* width, int* height,
+                              int* alpha) {
+    unsigned char buffer[WEBP_VP8_HEADER_SIZE];
+    const uint32_t contentSize = stream->getLength();
+    const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE);
+    const uint32_t read_bytes = (contentSize < WEBP_VP8_HEADER_SIZE) ?
+        contentSize : WEBP_VP8_HEADER_SIZE;
+    if (len != read_bytes) {
+        return false; // can't read enough
+    }
+
+    WebPBitstreamFeatures features;
+    VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &features);
+    if (status != VP8_STATUS_OK) {
+        return false; // Invalid WebP file.
+    }
+    *width = features.width;
+    *height = features.height;
+    *alpha = features.has_alpha;
+
+    // sanity check for image size that's about to be decoded.
+    {
+        Sk64 size;
+        size.setMul(*width, *height);
+        if (size.isNeg() || !size.is32()) {
+            return false;
+        }
+        // now check that if we are 4-bytes per pixel, we also don't overflow
+        if (size.get32() > (0x7FFFFFFF >> 2)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+class SkWEBPImageDecoder: public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kWEBP_Format;
+    }
+
+protected:
+    virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height);
+    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+
+private:
+    bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
+    SkStream *inputStream;
+    int origWidth;
+    int origHeight;
+    int hasAlpha;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+
+class AutoTimeMillis {
+public:
+    AutoTimeMillis(const char label[]) :
+        fLabel(label) {
+        if (!fLabel) {
+            fLabel = "";
+        }
+        fNow = SkTime::GetMSecs();
+    }
+    ~AutoTimeMillis() {
+        SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
+    }
+private:
+    const char* fLabel;
+    SkMSec fNow;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// This guy exists just to aid in debugging, as it allows debuggers to just
+// set a break-point in one place to see all error exists.
+static bool return_false(const SkBitmap& bm, const char msg[]) {
+#if 0
+    SkDebugf("libwebp error %s [%d %d]", msg, bm.width(), bm.height());
+#endif
+    return false; // must always return false
+}
+
+static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap, int hasAlpha) {
+    WEBP_CSP_MODE mode = MODE_LAST;
+    SkBitmap::Config config = decodedBitmap->config();
+    // For images that have alpha, choose appropriate color mode (MODE_rgbA,
+    // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency
+    // factor (alpha).
+    if (config == SkBitmap::kARGB_8888_Config) {
+      mode = hasAlpha ? MODE_rgbA : MODE_RGBA;
+    } else if (config == SkBitmap::kARGB_4444_Config) {
+      mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444;
+    } else if (config == SkBitmap::kRGB_565_Config) {
+      mode = MODE_RGB_565;
+    }
+    SkASSERT(mode != MODE_LAST);
+    return mode;
+}
+
+// Incremental WebP image decoding. Reads input buffer of 64K size iteratively
+// and decodes this block to appropriate color-space as per config object.
+static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) {
+    WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
+    if (idec == NULL) {
+        WebPFreeDecBuffer(&config.output);
+        return false;
+    }
+
+    stream->rewind();
+    const uint32_t contentSize = stream->getLength();
+    const uint32_t read_buffer_size = (contentSize < WEBP_IDECODE_BUFFER_SZ) ?
+        contentSize : WEBP_IDECODE_BUFFER_SZ;
+    SkAutoMalloc srcStorage(read_buffer_size);
+    unsigned char* input = (uint8_t*)srcStorage.get();
+    if (input == NULL) {
+        WebPIDelete(idec);
+        WebPFreeDecBuffer(&config.output);
+        return false;
+    }
+
+    uint32_t bytes_remaining = contentSize;
+    while (bytes_remaining > 0) {
+        const uint32_t bytes_to_read =
+            (bytes_remaining < WEBP_IDECODE_BUFFER_SZ) ?
+                bytes_remaining : WEBP_IDECODE_BUFFER_SZ;
+
+        const size_t bytes_read = stream->read(input, bytes_to_read);
+        if (bytes_read == 0) {
+            break;
+        }
+
+        VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
+        if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
+            bytes_remaining -= bytes_read;
+        } else {
+            break;
+        }
+    }
+    srcStorage.free();
+    WebPIDelete(idec);
+    WebPFreeDecBuffer(&config.output);
+
+    if (bytes_remaining > 0) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+static bool webp_get_config_resize(WebPDecoderConfig& config,
+                                   SkBitmap* decodedBitmap,
+                                   int width, int height, int hasAlpha) {
+    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha);
+    if (mode == MODE_LAST) {
+        return false;
+    }
+
+    if (WebPInitDecoderConfig(&config) == 0) {
+        return false;
+    }
+
+    config.output.colorspace = mode;
+    config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
+    config.output.u.RGBA.stride = decodedBitmap->rowBytes();
+    config.output.u.RGBA.size = decodedBitmap->getSize();
+    config.output.is_external_memory = 1;
+
+    if (width != decodedBitmap->width() ||
+        height != decodedBitmap->height()) {
+        config.options.use_scaling = 1;
+        config.options.scaled_width = decodedBitmap->width();
+        config.options.scaled_height = decodedBitmap->height();
+    }
+
+    return true;
+}
+
+static bool webp_get_config_resize_crop(WebPDecoderConfig& config,
+                                        SkBitmap* decodedBitmap,
+                                        SkIRect region, int hasAlpha) {
+
+    if (!webp_get_config_resize(
+        config, decodedBitmap, region.width(), region.height(), hasAlpha)) {
+      return false;
+    }
+
+    config.options.use_cropping = 1;
+    config.options.crop_left = region.fLeft;
+    config.options.crop_top = region.fTop;
+    config.options.crop_width = region.width();
+    config.options.crop_height = region.height();
+
+    return true;
+}
+
+bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
+                                         int width, int height) {
+    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
+
+    // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
+    if (hasAlpha) {
+        if (config != SkBitmap::kARGB_4444_Config) {
+            config = SkBitmap::kARGB_8888_Config;
+        }
+    } else {
+        if (config != SkBitmap::kRGB_565_Config &&
+            config != SkBitmap::kARGB_4444_Config) {
+            config = SkBitmap::kARGB_8888_Config;
+        }
+    }
+
+    if (!this->chooseFromOneChoice(config, width, height)) {
+        return false;
+    }
+
+    decodedBitmap->setConfig(config, width, height, 0);
+
+    decodedBitmap->setIsOpaque(!hasAlpha);
+
+    return true;
+}
+
+bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream,
+                                          int *width, int *height) {
+    int origWidth, origHeight, hasAlpha;
+    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
+        return false;
+    }
+
+    stream->rewind();
+    *width = origWidth;
+    *height = origHeight;
+
+    this->inputStream = stream;
+    this->origWidth = origWidth;
+    this->origHeight = origHeight;
+    this->hasAlpha = hasAlpha;
+
+    return true;
+}
+
+static bool isConfigCompatible(SkBitmap* bitmap) {
+    SkBitmap::Config config = bitmap->config();
+    return config == SkBitmap::kARGB_4444_Config ||
+           config == SkBitmap::kRGB_565_Config ||
+           config == SkBitmap::kARGB_8888_Config;
+}
+
+bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap,
+                                        SkIRect region) {
+    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
+
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, just
+        // returns false
+        return false;
+    }
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
+    const int width = sampler.scaledWidth();
+    const int height = sampler.scaledHeight();
+
+    // The image can be decoded directly to decodedBitmap if
+    //   1. the region is within the image range
+    //   2. bitmap's config is compatible
+    //   3. bitmap's size is same as the required region (after sampled)
+    bool directDecode = (rect == region) &&
+                        (decodedBitmap->isNull() ||
+                         (isConfigCompatible(decodedBitmap) &&
+                         (decodedBitmap->width() == width) &&
+                         (decodedBitmap->height() == height)));
+    SkTScopedPtr<SkBitmap> adb;
+    SkBitmap *bitmap = decodedBitmap;
+
+    if (!directDecode) {
+        // allocates a temp bitmap
+        bitmap = new SkBitmap;
+        adb.reset(bitmap);
+    }
+
+    if (bitmap->isNull()) {
+        if (!setDecodeConfig(bitmap, width, height)) {
+            return false;
+        }
+        // alloc from native heap if it is a temp bitmap. (prevent GC)
+        bool allocResult = (bitmap == decodedBitmap)
+                               ? allocPixelRef(bitmap, NULL)
+                               : bitmap->allocPixels();
+        if (!allocResult) {
+            return return_false(*decodedBitmap, "allocPixelRef");
+        }
+    } else {
+        // This is also called in setDecodeConfig in above block.
+        // i.e., when bitmap->isNull() is true.
+        if (!chooseFromOneChoice(bitmap->config(), width, height)) {
+            return false;
+        }
+    }
+
+    SkAutoLockPixels alp(*bitmap);
+    WebPDecoderConfig config;
+    if (!webp_get_config_resize_crop(config, bitmap, rect, hasAlpha)) {
+        return false;
+    }
+
+    // Decode the WebP image data stream using WebP incremental decoding for
+    // the specified cropped image-region.
+    if (!webp_idecode(this->inputStream, config)) {
+        return false;
+    }
+
+    if (!directDecode) {
+        cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
+                  region.width(), region.height(), rect.x(), rect.y());
+    }
+    return true;
+}
+
+bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+                                  Mode mode) {
+#ifdef TIME_DECODE
+    AutoTimeMillis atm("WEBP Decode");
+#endif
+
+    int origWidth, origHeight, hasAlpha;
+    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
+        return false;
+    }
+    this->hasAlpha = hasAlpha;
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+    // If only bounds are requested, done
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
+                             sampler.scaledHeight())) {
+            return false;
+        }
+        return true;
+    }
+#ifdef SK_BUILD_FOR_ANDROID
+    // No Bitmap reuse supported for this format
+    if (!decodedBitmap->isNull()) {
+        return false;
+    }
+#endif
+    if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
+                         sampler.scaledHeight())) {
+        return false;
+    }
+
+    if (!this->allocPixelRef(decodedBitmap, NULL)) {
+        return return_false(*decodedBitmap, "allocPixelRef");
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    WebPDecoderConfig config;
+    if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight,
+                                hasAlpha)) {
+        return false;
+    }
+
+    // Decode the WebP image data stream using WebP incremental decoding.
+    return webp_idecode(stream, config);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
+                                 const SkPMColor* SK_RESTRICT ctable);
+
+static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                             const SkPMColor*) {
+  const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint32_t c = *src++;
+      rgb[0] = SkGetPackedR32(c);
+      rgb[1] = SkGetPackedG32(c);
+      rgb[2] = SkGetPackedB32(c);
+      rgb += 3;
+  }
+}
+
+static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                           const SkPMColor*) {
+  const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint16_t c = *src++;
+      rgb[0] = SkPacked16ToR32(c);
+      rgb[1] = SkPacked16ToG32(c);
+      rgb[2] = SkPacked16ToB32(c);
+      rgb += 3;
+  }
+}
+
+static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                             const SkPMColor*) {
+  const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
+  for (int i = 0; i < width; ++i) {
+      const SkPMColor16 c = *src++;
+      rgb[0] = SkPacked4444ToR32(c);
+      rgb[1] = SkPacked4444ToG32(c);
+      rgb[2] = SkPacked4444ToB32(c);
+      rgb += 3;
+  }
+}
+
+static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
+                          const SkPMColor* SK_RESTRICT ctable) {
+  const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint32_t c = ctable[*src++];
+      rgb[0] = SkGetPackedR32(c);
+      rgb[1] = SkGetPackedG32(c);
+      rgb[2] = SkGetPackedB32(c);
+      rgb += 3;
+  }
+}
+
+static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) {
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            return ARGB_8888_To_RGB;
+        case SkBitmap::kRGB_565_Config:
+            return RGB_565_To_RGB;
+        case SkBitmap::kARGB_4444_Config:
+            return ARGB_4444_To_RGB;
+        case SkBitmap::kIndex8_Config:
+            return Index8_To_RGB;
+        default:
+            return NULL;
+    }
+}
+
+static int StreamWriter(const uint8_t* data, size_t data_size,
+                        const WebPPicture* const picture) {
+  SkWStream* const stream = (SkWStream*)picture->custom_ptr;
+  return stream->write(data, data_size) ? 1 : 0;
+}
+
+class SkWEBPImageEncoder : public SkImageEncoder {
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+};
+
+bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
+                                  int quality) {
+    const SkBitmap::Config config = bm.getConfig();
+    const ScanlineImporter scanline_import = ChooseImporter(config);
+    if (NULL == scanline_import) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(bm);
+    SkAutoLockColors ctLocker;
+    if (NULL == bm.getPixels()) {
+        return false;
+    }
+
+    WebPConfig webp_config;
+    if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) {
+        return false;
+    }
+
+    WebPPicture pic;
+    WebPPictureInit(&pic);
+    pic.width = bm.width();
+    pic.height = bm.height();
+    pic.writer = StreamWriter;
+    pic.custom_ptr = (void*)stream;
+
+    const SkPMColor* colors = ctLocker.lockColors(bm);
+    const uint8_t* src = (uint8_t*)bm.getPixels();
+    const int rgb_stride = pic.width * 3;
+
+    // Import (for each scanline) the bit-map image (in appropriate color-space)
+    // to RGB color space.
+    uint8_t* rgb = new uint8_t[rgb_stride * pic.height];
+    for (int y = 0; y < pic.height; ++y) {
+        scanline_import(src + y * bm.rowBytes(), rgb + y * rgb_stride,
+                        pic.width, colors);
+    }
+
+    bool ok = WebPPictureImportRGB(&pic, rgb, rgb_stride);
+    delete[] rgb;
+
+    ok = ok && WebPEncode(&webp_config, &pic);
+    WebPPictureFree(&pic);
+
+    return ok;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* DFactory(SkStream* stream) {
+    int width, height, hasAlpha;
+    if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
+        return NULL;
+    }
+
+    // Magic matches, call decoder
+    return SkNEW(SkWEBPImageDecoder);
+}
+
+SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
+    return DFactory(stream);
+}
+
+static SkImageEncoder* 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/legacy/src/images/SkImageDecoder_wbmp.cpp b/legacy/src/images/SkImageDecoder_wbmp.cpp
new file mode 100644
index 0000000..1ec82d9
--- /dev/null
+++ b/legacy/src/images/SkImageDecoder_wbmp.cpp
@@ -0,0 +1,173 @@
+
+/*
+ * 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 "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkMath.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+class SkWBMPImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kWBMP_Format;
+    }
+    
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+};
+
+static bool read_byte(SkStream* stream, uint8_t* data)
+{
+    return stream->read(data, 1) == 1;
+}
+
+static bool read_mbf(SkStream* stream, int* value)
+{
+    int n = 0;
+    uint8_t data;
+    do {
+        if (!read_byte(stream, &data)) {
+            return false;
+        }
+        n = (n << 7) | (data & 0x7F);
+    } while (data & 0x80);
+    
+    *value = n;
+    return true;
+}
+
+struct wbmp_head {
+    int fWidth;
+    int fHeight;
+    
+    bool init(SkStream* stream)
+    {
+        uint8_t data;
+        
+        if (!read_byte(stream, &data) || data != 0) { // unknown type
+            return false;
+        }
+        if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header
+            return false;
+        }
+        if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) {
+            return false;
+        }
+        if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) {
+            return false;
+        }
+        return fWidth != 0 && fHeight != 0;
+    }
+};
+    
+static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits)
+{
+    int bytes = bits >> 3;
+    
+    for (int i = 0; i < bytes; i++) {
+        unsigned mask = *src++;
+        dst[0] = (mask >> 7) & 1;
+        dst[1] = (mask >> 6) & 1;
+        dst[2] = (mask >> 5) & 1;
+        dst[3] = (mask >> 4) & 1;
+        dst[4] = (mask >> 3) & 1;
+        dst[5] = (mask >> 2) & 1;
+        dst[6] = (mask >> 1) & 1;
+        dst[7] = (mask >> 0) & 1;
+        dst += 8;
+    }
+    
+    bits &= 7;
+    if (bits > 0) {
+        unsigned mask = *src;
+        do {
+            *dst++ = (mask >> 7) & 1;;
+            mask <<= 1;
+        } while (--bits != 0);    
+    }
+}
+
+#define SkAlign8(x)     (((x) + 7) & ~7)
+
+bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+                                  Mode mode)
+{
+    wbmp_head   head;
+    
+    if (!head.init(stream)) {
+        return false;
+    }
+        
+    int width = head.fWidth;
+    int height = head.fHeight;
+    
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        // assign these directly, in case we return kDimensions_Result
+        decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+        decodedBitmap->setIsOpaque(true);
+        return true;
+    }
+#ifdef SK_BUILD_FOR_ANDROID
+    // No Bitmap reuse supported for this format
+    if (!decodedBitmap->isNull()) {
+        return false;
+    }
+#endif
+    // assign these directly, in case we return kDimensions_Result
+    decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+    decodedBitmap->setIsOpaque(true);
+
+    const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
+    SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
+    SkAutoUnref   aur(ct);
+
+    if (!this->allocPixelRef(decodedBitmap, ct)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    uint8_t* dst = decodedBitmap->getAddr8(0, 0);
+    // store the 1-bit valuess at the end of our pixels, so we won't stomp
+    // on them before we're read them. Just trying to avoid a temp allocation
+    size_t srcRB = SkAlign8(width) >> 3;
+    size_t srcSize = height * srcRB;
+    uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
+    if (stream->read(src, srcSize) != srcSize) {
+        return false;
+    }
+
+    for (int y = 0; y < height; y++)
+    {
+        expand_bits_to_bytes(dst, src, width);
+        dst += decodedBitmap->rowBytes();
+        src += srcRB;
+    }
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* Factory(SkStream* stream) {
+    wbmp_head   head;
+
+    if (head.init(stream)) {
+        return SkNEW(SkWBMPImageDecoder);
+    }
+    return NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+
diff --git a/legacy/src/images/SkImageEncoder.cpp b/legacy/src/images/SkImageEncoder.cpp
new file mode 100644
index 0000000..e05a28c
--- /dev/null
+++ b/legacy/src/images/SkImageEncoder.cpp
@@ -0,0 +1,41 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkImageEncoder.h"
+#include "SkBitmap.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+SkImageEncoder::~SkImageEncoder() {}
+
+bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm,
+                                  int quality) {
+    quality = SkMin32(100, SkMax32(0, quality));
+    return this->onEncode(stream, bm, quality);
+}
+
+bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm,
+                                int quality) {
+    quality = SkMin32(100, SkMax32(0, quality));
+    SkFILEWStream   stream(file);
+    return this->onEncode(&stream, bm, quality);
+}
+
+bool SkImageEncoder::EncodeFile(const char file[], const SkBitmap& bm, Type t,
+                                int quality) {
+    SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+    return enc.get() && enc.get()->encodeFile(file, bm, quality);
+}
+
+bool SkImageEncoder::EncodeStream(SkWStream* stream, const SkBitmap& bm, Type t,
+                                int quality) {
+    SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+    return enc.get() && enc.get()->encodeStream(stream, bm, quality);
+}
+
diff --git a/legacy/src/images/SkImageEncoder_Factory.cpp b/legacy/src/images/SkImageEncoder_Factory.cpp
new file mode 100644
index 0000000..2bd1113
--- /dev/null
+++ b/legacy/src/images/SkImageEncoder_Factory.cpp
@@ -0,0 +1,37 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkImageEncoder.h"
+#include "SkTRegistry.h"
+
+typedef SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> EncodeReg;
+
+// Can't use the typedef here because of complex C++ corner cases
+template EncodeReg* SkTRegistry<SkImageEncoder*, SkImageEncoder::Type>::gHead;
+
+#ifdef SK_ENABLE_LIBPNG
+    extern SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type);
+#endif
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+    SkImageEncoder* codec = NULL;
+    const EncodeReg* curr = EncodeReg::Head();
+    while (curr) {
+        if ((codec = curr->factory()(t)) != NULL) {
+            return codec;
+        }
+        curr = curr->next();
+    }
+#ifdef SK_ENABLE_LIBPNG
+    if ((codec = sk_libpng_efactory(t)) != NULL) {
+        return codec;
+    }
+#endif
+    return NULL;
+}
diff --git a/legacy/src/images/SkImageRef.cpp b/legacy/src/images/SkImageRef.cpp
new file mode 100644
index 0000000..1d6b270
--- /dev/null
+++ b/legacy/src/images/SkImageRef.cpp
@@ -0,0 +1,203 @@
+
+/*
+ * 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.h"
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+//#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) {
+    SkASSERT(stream);
+    stream->ref();
+    fStream = stream;
+    fConfig = config;
+    fSampleSize = sampleSize;
+    fDoDither = true;
+    fPrev = fNext = NULL;
+    fFactory = NULL;
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    SkDebugf("add ImageRef %p [%d] data=%d\n",
+              this, config, (int)stream->getLength());
+#endif
+}
+
+SkImageRef::~SkImageRef() {
+    SkASSERT(&gImageRefMutex == this->mutex());
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    SkDebugf("delete ImageRef %p [%d] data=%d\n",
+              this, fConfig, (int)fStream->getLength());
+#endif
+
+    fStream->unref();
+    SkSafeUnref(fFactory);
+}
+
+bool SkImageRef::getInfo(SkBitmap* bitmap) {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+
+    if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) {
+        return false;
+    }
+
+    SkASSERT(SkBitmap::kNo_Config != fBitmap.config());
+    if (bitmap) {
+        bitmap->setConfig(fBitmap.config(), fBitmap.width(), fBitmap.height());
+    }
+    return true;
+}
+
+bool SkImageRef::isOpaque(SkBitmap* bitmap) {
+    if (bitmap && bitmap->pixelRef() == this) {
+        bitmap->lockPixels();
+        bitmap->setIsOpaque(fBitmap.isOpaque());
+        bitmap->unlockPixels();
+        return true;
+    }
+    return false;
+}
+
+SkImageDecoderFactory* SkImageRef::setDecoderFactory(
+                                                SkImageDecoderFactory* fact) {
+    SkRefCnt_SafeAssign(fFactory, fact);
+    return fact;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream,
+                          SkBitmap* bitmap, SkBitmap::Config config,
+                          SkImageDecoder::Mode mode) {
+    return codec->decode(stream, bitmap, config, mode);
+}
+
+bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
+    SkASSERT(&gImageRefMutex == this->mutex());
+
+    if (fErrorInDecoding) {
+        return false;
+    }
+
+    /*  As soon as we really know our config, we record it, so that on
+        subsequent calls to the codec, we are sure we will always get the same
+        result.
+    */
+    if (SkBitmap::kNo_Config != fBitmap.config()) {
+        fConfig = fBitmap.config();
+    }
+
+    if (NULL != fBitmap.getPixels() ||
+            (SkBitmap::kNo_Config != fBitmap.config() &&
+             SkImageDecoder::kDecodeBounds_Mode == mode)) {
+        return true;
+    }
+
+    SkASSERT(fBitmap.getPixels() == NULL);
+
+    fStream->rewind();
+
+    SkImageDecoder* codec;
+    if (fFactory) {
+        codec = fFactory->newDecoder(fStream);
+    } else {
+        codec = SkImageDecoder::Factory(fStream);
+    }
+
+    if (codec) {
+        SkAutoTDelete<SkImageDecoder> ad(codec);
+
+        codec->setSampleSize(fSampleSize);
+        codec->setDitherImage(fDoDither);
+        if (this->onDecode(codec, fStream, &fBitmap, fConfig, mode)) {
+            return true;
+        }
+    }
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    if (NULL == codec) {
+        SkDebugf("--- ImageRef: <%s> failed to find codec\n", this->getURI());
+    } else {
+        SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n",
+                 this->getURI(), mode);
+    }
+#endif
+    fErrorInDecoding = true;
+    fBitmap.reset();
+    return false;
+}
+
+void* SkImageRef::onLockPixels(SkColorTable** ct) {
+    SkASSERT(&gImageRefMutex == this->mutex());
+
+    if (NULL == fBitmap.getPixels()) {
+        (void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode);
+    }
+
+    if (ct) {
+        *ct = fBitmap.getColorTable();
+    }
+    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;
+
+    if (fBitmap.getPixels()) {
+        size = fBitmap.getSize();
+        if (fBitmap.getColorTable()) {
+            size += fBitmap.getColorTable()->count() * sizeof(SkPMColor);
+        }
+    }
+    return size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, &gImageRefMutex), fErrorInDecoding(false) {
+    fConfig = (SkBitmap::Config)buffer.readU8();
+    fSampleSize = buffer.readU8();
+    fDoDither = buffer.readBool();
+
+    size_t length = buffer.readU32();
+    fStream = SkNEW_ARGS(SkMemoryStream, (length));
+    buffer.read((void*)fStream->getMemoryBase(), length);
+
+    fPrev = fNext = NULL;
+    fFactory = NULL;
+}
+
+void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.write8(fConfig);
+    buffer.write8(fSampleSize);
+    buffer.writeBool(fDoDither);
+    size_t length = fStream->getLength();
+    buffer.write32(length);
+    fStream->rewind();
+    buffer.readFromStream(fStream, length);
+}
+
diff --git a/legacy/src/images/SkImageRefPool.cpp b/legacy/src/images/SkImageRefPool.cpp
new file mode 100644
index 0000000..c24dba0
--- /dev/null
+++ b/legacy/src/images/SkImageRefPool.cpp
@@ -0,0 +1,193 @@
+
+/*
+ * 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 "SkImageRefPool.h"
+#include "SkImageRef.h"
+#include "SkThread.h"
+
+SkImageRefPool::SkImageRefPool() {
+    fRAMBudget = 0; // means no explicit limit
+    fRAMUsed = 0;
+    fCount = 0;
+    fHead = fTail = NULL;
+}
+
+SkImageRefPool::~SkImageRefPool() {
+    //    SkASSERT(NULL == fHead);
+}
+
+void SkImageRefPool::setRAMBudget(size_t size) {
+    if (fRAMBudget != size) {
+        fRAMBudget = size;
+        this->purgeIfNeeded();
+    }
+}
+
+void SkImageRefPool::justAddedPixels(SkImageRef* ref) {
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    SkDebugf("=== ImagePool: add pixels %s [%d %d %d] bytes=%d heap=%d\n",
+             ref->getURI(),
+             ref->fBitmap.width(), ref->fBitmap.height(),
+             ref->fBitmap.bytesPerPixel(),
+             ref->fBitmap.getSize(), (int)fRAMUsed);
+#endif
+    fRAMUsed += ref->ramUsed();
+    this->purgeIfNeeded();
+}
+
+void SkImageRefPool::canLosePixels(SkImageRef* ref) {
+    // the refs near fHead have recently been released (used)
+    // if we purge, we purge from the tail
+    this->detach(ref);
+    this->addToHead(ref);
+    this->purgeIfNeeded();
+}
+
+void SkImageRefPool::purgeIfNeeded() {
+    // do nothing if we have a zero-budget (i.e. unlimited)
+    if (fRAMBudget != 0) {
+        this->setRAMUsed(fRAMBudget);
+    }
+}
+
+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()) {
+            size_t size = ref->ramUsed();
+            SkASSERT(size <= fRAMUsed);
+            fRAMUsed -= size;
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+            SkDebugf("=== ImagePool: purge %s [%d %d %d] bytes=%d heap=%d\n",
+                     ref->getURI(),
+                     ref->fBitmap.width(), ref->fBitmap.height(),
+                     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);
+            SkASSERT(NULL == ref->fBitmap.getPixels());
+        }
+        ref = ref->fPrev;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+    }
+    if (fTail == ref) {
+        fTail = ref->fPrev;
+    }
+    if (ref->fPrev) {
+        ref->fPrev->fNext = ref->fNext;
+    }
+    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();
+}
+
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+void SkImageRefPool::dump() const {
+#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/legacy/src/images/SkImageRefPool.h b/legacy/src/images/SkImageRefPool.h
new file mode 100644
index 0000000..d29b8db
--- /dev/null
+++ b/legacy/src/images/SkImageRefPool.h
@@ -0,0 +1,50 @@
+
+/*
+ * 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 SkImageRefPool_DEFINED
+#define SkImageRefPool_DEFINED
+
+#include "SkTypes.h"
+
+class SkImageRef;
+class SkImageRef_GlobalPool;
+
+class SkImageRefPool {
+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/legacy/src/images/SkImageRef_GlobalPool.cpp b/legacy/src/images/SkImageRef_GlobalPool.cpp
new file mode 100644
index 0000000..b774023
--- /dev/null
+++ b/legacy/src/images/SkImageRef_GlobalPool.cpp
@@ -0,0 +1,106 @@
+
+/*
+ * 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_GlobalPool.h"
+#include "SkImageRefPool.h"
+#include "SkThread.h"
+
+extern SkBaseMutex gImageRefMutex;
+
+/*
+ *  This returns the lazily-allocated global pool. It must be called
+ *  from inside the guard mutex, so we safely only ever allocate 1.
+ */
+static SkImageRefPool* GetGlobalPool() {
+    static SkImageRefPool* gPool;
+    if (NULL == gPool) {
+        gPool = SkNEW(SkImageRefPool);
+        // call sk_atexit(...) when we have that, to free the global pool
+    }
+    return gPool;
+}
+
+SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkStream* stream,
+                                             SkBitmap::Config config,
+                                             int sampleSize)
+        : SkImageRef(stream, config, sampleSize) {
+    this->mutex()->acquire();
+    GetGlobalPool()->addToHead(this);
+    this->mutex()->release();
+}
+
+SkImageRef_GlobalPool::~SkImageRef_GlobalPool() {
+    this->mutex()->acquire();
+    GetGlobalPool()->detach(this);
+    this->mutex()->release();
+}
+
+/*  By design, onUnlockPixels() already is inside the mutex-lock,
+ *  and it is the (indirect) caller of onDecode(), therefore we can assume
+ *  that we also are already inside the mutex. Hence, we can reference
+ *  the global-pool directly.
+ */
+bool SkImageRef_GlobalPool::onDecode(SkImageDecoder* codec, SkStream* stream,
+                                     SkBitmap* bitmap, SkBitmap::Config config,
+                                     SkImageDecoder::Mode mode) {
+    if (!this->INHERITED::onDecode(codec, stream, bitmap, config, mode)) {
+        return false;
+    }
+    if (mode == SkImageDecoder::kDecodePixels_Mode) {
+        // no need to grab the mutex here, it has already been acquired.
+        GetGlobalPool()->justAddedPixels(this);
+    }
+    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();
+    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);
+    return GetGlobalPool()->getRAMBudget();
+}
+
+void SkImageRef_GlobalPool::SetRAMBudget(size_t size) {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    GetGlobalPool()->setRAMBudget(size);
+}
+
+size_t SkImageRef_GlobalPool::GetRAMUsed() {
+    SkAutoMutexAcquire ac(gImageRefMutex);    
+    return GetGlobalPool()->getRAMUsed();
+}
+
+void SkImageRef_GlobalPool::SetRAMUsed(size_t usage) {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    GetGlobalPool()->setRAMUsed(usage);
+}
+
+void SkImageRef_GlobalPool::DumpPool() {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    GetGlobalPool()->dump();
+}
diff --git a/legacy/src/images/SkJpegUtility.cpp b/legacy/src/images/SkJpegUtility.cpp
new file mode 100644
index 0000000..aa5237f
--- /dev/null
+++ b/legacy/src/images/SkJpegUtility.cpp
@@ -0,0 +1,207 @@
+
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkJpegUtility.h"
+
+/////////////////////////////////////////////////////////////////////
+static void sk_init_source(j_decompress_ptr cinfo) {
+    skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
+    src->next_input_byte = (const JOCTET*)src->fBuffer;
+    src->bytes_in_buffer = 0;
+    src->current_offset = 0;
+    src->fStream->rewind();
+}
+
+static boolean sk_seek_input_data(j_decompress_ptr cinfo, long byte_offset) {
+    skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
+
+    if (byte_offset > src->current_offset) {
+        (void)src->fStream->skip(byte_offset - src->current_offset);
+    } else {
+        src->fStream->rewind();
+        (void)src->fStream->skip(byte_offset);
+    }
+
+    src->current_offset = byte_offset;
+    src->next_input_byte = (const JOCTET*)src->fBuffer;
+    src->bytes_in_buffer = 0;
+    return TRUE;
+}
+
+static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
+    skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
+    if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
+        return FALSE;
+    }
+    size_t bytes = src->fStream->read(src->fBuffer, skjpeg_source_mgr::kBufferSize);
+    // note that JPEG is happy with less than the full read,
+    // as long as the result is non-zero
+    if (bytes == 0) {
+        return FALSE;
+    }
+
+    src->current_offset += bytes;
+    src->next_input_byte = (const JOCTET*)src->fBuffer;
+    src->bytes_in_buffer = bytes;
+    return TRUE;
+}
+
+static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+    skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
+
+    if (num_bytes > (long)src->bytes_in_buffer) {
+        long bytesToSkip = num_bytes - src->bytes_in_buffer;
+        while (bytesToSkip > 0) {
+            long bytes = (long)src->fStream->skip(bytesToSkip);
+            if (bytes <= 0 || bytes > bytesToSkip) {
+//              SkDebugf("xxxxxxxxxxxxxx failure to skip request %d returned %d\n", bytesToSkip, bytes);
+                cinfo->err->error_exit((j_common_ptr)cinfo);
+                return;
+            }
+            src->current_offset += bytes;
+            bytesToSkip -= bytes;
+        }
+        src->next_input_byte = (const JOCTET*)src->fBuffer;
+        src->bytes_in_buffer = 0;
+    } else {
+        src->next_input_byte += num_bytes;
+        src->bytes_in_buffer -= num_bytes;
+    }
+}
+
+static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) {
+    skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
+
+    // what is the desired param for???
+
+    if (!src->fStream->rewind()) {
+        SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
+        cinfo->err->error_exit((j_common_ptr)cinfo);
+        return FALSE;
+    }
+    src->next_input_byte = (const JOCTET*)src->fBuffer;
+    src->bytes_in_buffer = 0;
+    return TRUE;
+}
+
+static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
+
+
+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;
+    src->start_input_byte = (const JOCTET*)src->fMemoryBase;
+    src->bytes_in_buffer = src->fMemoryBaseSize;
+    src->current_offset = src->fMemoryBaseSize;
+}
+
+static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
+    SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n");
+    return FALSE;
+}
+
+static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+    skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
+//    SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes);
+    src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
+    src->bytes_in_buffer -= num_bytes;
+}
+
+static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) {
+    SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n");
+    return TRUE;
+}
+
+static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder,
+                                     bool ownStream) : fStream(stream) {
+    fDecoder = decoder;
+    const void* baseAddr = stream->getMemoryBase();
+    size_t bufferSize = 4096;
+    size_t len;
+    fMemoryBase = NULL;
+    fUnrefStream = ownStream;
+    fMemoryBaseSize = 0;
+
+    init_source = sk_init_source;
+    fill_input_buffer = sk_fill_input_buffer;
+    skip_input_data = sk_skip_input_data;
+    resync_to_restart = sk_resync_to_restart;
+    term_source = sk_term_source;
+    seek_input_data = sk_seek_input_data;
+//    SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
+}
+
+skjpeg_source_mgr::~skjpeg_source_mgr() {
+    if (fMemoryBase) {
+        sk_free(fMemoryBase);
+    }
+    if (fUnrefStream) {
+        fStream->unref();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void sk_init_destination(j_compress_ptr cinfo) {
+    skjpeg_destination_mgr* dest = (skjpeg_destination_mgr*)cinfo->dest;
+
+    dest->next_output_byte = dest->fBuffer;
+    dest->free_in_buffer = skjpeg_destination_mgr::kBufferSize;
+}
+
+static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
+    skjpeg_destination_mgr* dest = (skjpeg_destination_mgr*)cinfo->dest;
+
+//  if (!dest->fStream->write(dest->fBuffer, skjpeg_destination_mgr::kBufferSize - dest->free_in_buffer))
+    if (!dest->fStream->write(dest->fBuffer,
+            skjpeg_destination_mgr::kBufferSize)) {
+        ERREXIT(cinfo, JERR_FILE_WRITE);
+        return false;
+    }
+
+    dest->next_output_byte = dest->fBuffer;
+    dest->free_in_buffer = skjpeg_destination_mgr::kBufferSize;
+    return TRUE;
+}
+
+static void sk_term_destination (j_compress_ptr cinfo) {
+    skjpeg_destination_mgr* dest = (skjpeg_destination_mgr*)cinfo->dest;
+
+    size_t size = skjpeg_destination_mgr::kBufferSize - dest->free_in_buffer;
+    if (size > 0) {
+        if (!dest->fStream->write(dest->fBuffer, size)) {
+            ERREXIT(cinfo, JERR_FILE_WRITE);
+            return;
+        }
+    }
+    dest->fStream->flush();
+}
+
+skjpeg_destination_mgr::skjpeg_destination_mgr(SkWStream* stream)
+        : fStream(stream) {
+    this->init_destination = sk_init_destination;
+    this->empty_output_buffer = sk_empty_output_buffer;
+    this->term_destination = sk_term_destination;
+}
+
+void skjpeg_error_exit(j_common_ptr cinfo) {
+    skjpeg_error_mgr* error = (skjpeg_error_mgr*)cinfo->err;
+
+    (*error->output_message) (cinfo);
+
+    /* Let the memory manager delete any temp files before we die */
+    jpeg_destroy(cinfo);
+
+    longjmp(error->fJmpBuf, -1);
+}
diff --git a/legacy/src/images/SkMovie.cpp b/legacy/src/images/SkMovie.cpp
new file mode 100644
index 0000000..81820a5
--- /dev/null
+++ b/legacy/src/images/SkMovie.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 "SkMovie.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+// 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)
+
+SkMovie::SkMovie()
+{
+    fInfo.fDuration = UNINITIALIZED_MSEC;  // uninitialized
+    fCurrTime = UNINITIALIZED_MSEC; // uninitialized
+    fNeedBitmap = true;
+}
+
+void SkMovie::ensureInfo()
+{
+    if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo))
+        memset(&fInfo, 0, sizeof(fInfo));   // failure
+}
+
+SkMSec SkMovie::duration()
+{
+    this->ensureInfo();
+    return fInfo.fDuration;
+}
+
+int SkMovie::width()
+{
+    this->ensureInfo();
+    return fInfo.fWidth;
+}
+
+int SkMovie::height()
+{
+    this->ensureInfo();
+    return fInfo.fHeight;
+}
+
+int SkMovie::isOpaque()
+{
+    this->ensureInfo();
+    return fInfo.fIsOpaque;
+}
+
+bool SkMovie::setTime(SkMSec time)
+{
+    SkMSec dur = this->duration();
+    if (time > dur)
+        time = dur;
+        
+    bool changed = false;
+    if (time != fCurrTime)
+    {
+        fCurrTime = time;
+        changed = this->onSetTime(time);
+        fNeedBitmap |= changed;
+    }
+    return changed;
+}
+
+const SkBitmap& SkMovie::bitmap()
+{
+    if (fCurrTime == UNINITIALIZED_MSEC)    // uninitialized
+        this->setTime(0);
+
+    if (fNeedBitmap)
+    {
+        if (!this->onGetBitmap(&fBitmap))   // failure
+            fBitmap.reset();
+        fNeedBitmap = false;
+    }
+    return fBitmap;
+}
+
+////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+SkMovie* SkMovie::DecodeMemory(const void* data, size_t length) {
+    SkMemoryStream stream(data, length, false);
+    return SkMovie::DecodeStream(&stream);
+}
+
+SkMovie* SkMovie::DecodeFile(const char path[])
+{
+    SkMovie* movie = NULL;
+
+    SkFILEStream stream(path);
+    if (stream.isValid()) {
+        movie = SkMovie::DecodeStream(&stream);
+    }
+#ifdef SK_DEBUG
+    else {
+        SkDebugf("Movie file not found <%s>\n", path);
+    }
+#endif
+
+    return movie;
+}
+
diff --git a/legacy/src/images/SkMovie_gif.cpp b/legacy/src/images/SkMovie_gif.cpp
new file mode 100644
index 0000000..91d3591
--- /dev/null
+++ b/legacy/src/images/SkMovie_gif.cpp
@@ -0,0 +1,443 @@
+
+/*
+ * 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 "SkMovie.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+#include "gif_lib.h"
+
+class SkGIFMovie : public SkMovie {
+public:
+    SkGIFMovie(SkStream* stream);
+    virtual ~SkGIFMovie();
+
+protected:
+    virtual bool onGetInfo(Info*);
+    virtual bool onSetTime(SkMSec);
+    virtual bool onGetBitmap(SkBitmap*);
+    
+private:
+    GifFileType* fGIF;
+    int fCurrIndex;
+    int fLastDrawIndex;
+    SkBitmap fBackup;
+};
+
+static int Decode(GifFileType* fileType, GifByteType* out, int size) {
+    SkStream* stream = (SkStream*) fileType->UserData;
+    return (int) stream->read(out, size);
+}
+
+SkGIFMovie::SkGIFMovie(SkStream* stream)
+{
+    fGIF = DGifOpen( stream, Decode );
+    if (NULL == fGIF)
+        return;
+
+    if (DGifSlurp(fGIF) != GIF_OK)
+    {
+        DGifCloseFile(fGIF);
+        fGIF = NULL;
+    }
+    fCurrIndex = -1;
+    fLastDrawIndex = -1;
+}
+
+SkGIFMovie::~SkGIFMovie()
+{
+    if (fGIF)
+        DGifCloseFile(fGIF);
+}
+
+static SkMSec savedimage_duration(const SavedImage* image)
+{
+    for (int j = 0; j < image->ExtensionBlockCount; j++)
+    {
+        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
+        {
+            int size = image->ExtensionBlocks[j].ByteCount;
+            SkASSERT(size >= 4);
+            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
+            return ((b[2] << 8) | b[1]) * 10;
+        }
+    }
+    return 0;
+}
+
+bool SkGIFMovie::onGetInfo(Info* info)
+{
+    if (NULL == fGIF)
+        return false;
+
+    SkMSec dur = 0;
+    for (int i = 0; i < fGIF->ImageCount; i++)
+        dur += savedimage_duration(&fGIF->SavedImages[i]);
+
+    info->fDuration = dur;
+    info->fWidth = fGIF->SWidth;
+    info->fHeight = fGIF->SHeight;
+    info->fIsOpaque = false;    // how to compute?
+    return true;
+}
+
+bool SkGIFMovie::onSetTime(SkMSec time)
+{
+    if (NULL == fGIF)
+        return false;
+
+    SkMSec dur = 0;
+    for (int i = 0; i < fGIF->ImageCount; i++)
+    {
+        dur += savedimage_duration(&fGIF->SavedImages[i]);
+        if (dur >= time)
+        {
+            fCurrIndex = i;
+            return fLastDrawIndex != fCurrIndex;
+        }
+    }
+    fCurrIndex = fGIF->ImageCount - 1;
+    return true;
+}
+
+static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
+                     int transparent, int width)
+{
+    for (; width > 0; width--, src++, dst++) {
+        if (*src != transparent) {
+            const GifColorType& col = cmap->Colors[*src];
+            *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
+        }
+    }
+}
+
+static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
+                               const ColorMapObject* cmap, int transparent, int copyWidth,
+                               int copyHeight, const GifImageDesc& imageDesc, int rowStep,
+                               int startRow)
+{
+    int row;
+    // every 'rowStep'th row, starting with row 'startRow'
+    for (row = startRow; row < copyHeight; row += rowStep) {
+        uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
+        copyLine(dst, src, cmap, transparent, copyWidth);
+        src += imageDesc.Width;
+    }
+
+    // pad for rest height
+    src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
+}
+
+static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
+                          int transparent)
+{
+    int width = bm->width();
+    int height = bm->height();
+    GifWord copyWidth = frame->ImageDesc.Width;
+    if (frame->ImageDesc.Left + copyWidth > width) {
+        copyWidth = width - frame->ImageDesc.Left;
+    }
+
+    GifWord copyHeight = frame->ImageDesc.Height;
+    if (frame->ImageDesc.Top + copyHeight > height) {
+        copyHeight = height - frame->ImageDesc.Top;
+    }
+
+    // deinterlace
+    const unsigned char* src = (unsigned char*)frame->RasterBits;
+
+    // group 1 - every 8th row, starting with row 0
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
+
+    // group 2 - every 8th row, starting with row 4
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
+
+    // group 3 - every 4th row, starting with row 2
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
+
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
+}
+
+static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
+                       int transparent)
+{
+    int width = bm->width();
+    int height = bm->height();
+    const unsigned char* src = (unsigned char*)frame->RasterBits;
+    uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
+    GifWord copyWidth = frame->ImageDesc.Width;
+    if (frame->ImageDesc.Left + copyWidth > width) {
+        copyWidth = width - frame->ImageDesc.Left;
+    }
+
+    GifWord copyHeight = frame->ImageDesc.Height;
+    if (frame->ImageDesc.Top + copyHeight > height) {
+        copyHeight = height - frame->ImageDesc.Top;
+    }
+
+    int srcPad, dstPad;
+    dstPad = width - copyWidth;
+    srcPad = frame->ImageDesc.Width - copyWidth;
+    for (; copyHeight > 0; copyHeight--) {
+        copyLine(dst, src, cmap, transparent, copyWidth);
+        src += frame->ImageDesc.Width;
+        dst += width;
+    }
+}
+
+static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
+                     uint32_t col)
+{
+    int bmWidth = bm->width();
+    int bmHeight = bm->height();
+    uint32_t* dst = bm->getAddr32(left, top);
+    GifWord copyWidth = width;
+    if (left + copyWidth > bmWidth) {
+        copyWidth = bmWidth - left;
+    }
+
+    GifWord copyHeight = height;
+    if (top + copyHeight > bmHeight) {
+        copyHeight = bmHeight - top;
+    }
+
+    for (; copyHeight > 0; copyHeight--) {
+        sk_memset32(dst, col, copyWidth);
+        dst += bmWidth;
+    }
+}
+
+static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
+{
+    int transparent = -1;
+
+    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+        ExtensionBlock* eb = frame->ExtensionBlocks + i;
+        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+            eb->ByteCount == 4) {
+            bool has_transparency = ((eb->Bytes[0] & 1) == 1);
+            if (has_transparency) {
+                transparent = (unsigned char)eb->Bytes[3];
+            }
+        }
+    }
+
+    if (frame->ImageDesc.ColorMap != NULL) {
+        // use local color table
+        cmap = frame->ImageDesc.ColorMap;
+    }
+
+    if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+        SkDEBUGFAIL("bad colortable setup");
+        return;
+    }
+
+    if (frame->ImageDesc.Interlace) {
+        blitInterlace(bm, frame, cmap, transparent);
+    } else {
+        blitNormal(bm, frame, cmap, transparent);
+    }
+}
+
+static bool checkIfWillBeCleared(const SavedImage* frame)
+{
+    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+        ExtensionBlock* eb = frame->ExtensionBlocks + i;
+        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+            eb->ByteCount == 4) {
+            // check disposal method
+            int disposal = ((eb->Bytes[0] >> 2) & 7);
+            if (disposal == 2 || disposal == 3) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
+{
+    *trans = false;
+    *disposal = 0;
+    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+        ExtensionBlock* eb = frame->ExtensionBlocks + i;
+        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+            eb->ByteCount == 4) {
+            *trans = ((eb->Bytes[0] & 1) == 1);
+            *disposal = ((eb->Bytes[0] >> 2) & 7);
+        }
+    }
+}
+
+// return true if area of 'target' is completely covers area of 'covered'
+static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
+{
+    if (target->ImageDesc.Left <= covered->ImageDesc.Left
+        && covered->ImageDesc.Left + covered->ImageDesc.Width <=
+               target->ImageDesc.Left + target->ImageDesc.Width
+        && target->ImageDesc.Top <= covered->ImageDesc.Top
+        && covered->ImageDesc.Top + covered->ImageDesc.Height <=
+               target->ImageDesc.Top + target->ImageDesc.Height) {
+        return true;
+    }
+    return false;
+}
+
+static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
+                                 SkBitmap* backup, SkColor color)
+{
+    // We can skip disposal process if next frame is not transparent
+    // and completely covers current area
+    bool curTrans;
+    int curDisposal;
+    getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
+    bool nextTrans;
+    int nextDisposal;
+    getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
+    if ((curDisposal == 2 || curDisposal == 3)
+        && (nextTrans || !checkIfCover(next, cur))) {
+        switch (curDisposal) {
+        // restore to background color
+        // -> 'background' means background under this image.
+        case 2:
+            fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
+                     cur->ImageDesc.Width, cur->ImageDesc.Height,
+                     color);
+            break;
+
+        // restore to previous
+        case 3:
+            bm->swap(*backup);
+            break;
+        }
+    }
+
+    // Save current image if next frame's disposal method == 3
+    if (nextDisposal == 3) {
+        const uint32_t* src = bm->getAddr32(0, 0);
+        uint32_t* dst = backup->getAddr32(0, 0);
+        int cnt = bm->width() * bm->height();
+        memcpy(dst, src, cnt*sizeof(uint32_t));
+    }
+}
+
+bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
+{
+    const GifFileType* gif = fGIF;
+    if (NULL == gif)
+        return false;
+
+    if (gif->ImageCount < 1) {
+        return false;
+    }
+
+    const int width = gif->SWidth;
+    const int height = gif->SHeight;
+    if (width <= 0 || height <= 0) {
+        return false;
+    }
+
+    // no need to draw
+    if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
+        return true;
+    }
+
+    int startIndex = fLastDrawIndex + 1;
+    if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
+        // first time
+
+        startIndex = 0;
+
+        // create bitmap
+        bm->setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
+        if (!bm->allocPixels(NULL)) {
+            return false;
+        }
+        // create bitmap for backup
+        fBackup.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
+        if (!fBackup.allocPixels(NULL)) {
+            return false;
+        }
+    } else if (startIndex > fCurrIndex) {
+        // rewind to 1st frame for repeat
+        startIndex = 0;
+    }
+
+    int lastIndex = fCurrIndex;
+    if (lastIndex < 0) {
+        // first time
+        lastIndex = 0;
+    } else if (lastIndex > fGIF->ImageCount - 1) {
+        // this block must not be reached.
+        lastIndex = fGIF->ImageCount - 1;
+    }
+
+    SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
+    if (gif->SColorMap != NULL) {
+        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
+        bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
+    }
+
+    static SkColor paintingColor = SkPackARGB32(0, 0, 0, 0);
+    // draw each frames - not intelligent way
+    for (int i = startIndex; i <= lastIndex; i++) {
+        const SavedImage* cur = &fGIF->SavedImages[i];
+        if (i == 0) {
+            bool trans;
+            int disposal;
+            getTransparencyAndDisposalMethod(cur, &trans, &disposal);
+            if (!trans && gif->SColorMap != NULL) {
+                paintingColor = bgColor;
+            } else {
+                paintingColor = SkColorSetARGB(0, 0, 0, 0);
+            }
+
+            bm->eraseColor(paintingColor);
+            fBackup.eraseColor(paintingColor);
+        } else {
+            // Dispose previous frame before move to next frame.
+            const SavedImage* prev = &fGIF->SavedImages[i-1];
+            disposeFrameIfNeeded(bm, prev, cur, &fBackup, paintingColor);
+        }
+
+        // Draw frame
+        // We can skip this process if this index is not last and disposal
+        // method == 2 or method == 3
+        if (i == lastIndex || !checkIfWillBeCleared(cur)) {
+            drawFrame(bm, cur, gif->SColorMap);
+        }
+    }
+
+    // save index
+    fLastDrawIndex = lastIndex;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+SkMovie* Factory(SkStream* stream) {
+    char buf[GIF_STAMP_LEN];
+    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+            // must rewind here, since our construct wants to re-read the data
+            stream->rewind();
+            return SkNEW_ARGS(SkGIFMovie, (stream));
+        }
+    }
+    return NULL;
+}
+
+static SkTRegistry<SkMovie*, SkStream*> gReg(Factory);
diff --git a/legacy/src/images/SkPageFlipper.cpp b/legacy/src/images/SkPageFlipper.cpp
new file mode 100644
index 0000000..8e51e63
--- /dev/null
+++ b/legacy/src/images/SkPageFlipper.cpp
@@ -0,0 +1,78 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkPageFlipper.h"
+
+SkPageFlipper::SkPageFlipper() {
+    fWidth = 0;
+    fHeight = 0;
+    fDirty0 = &fDirty0Storage;
+    fDirty1 = &fDirty1Storage;
+
+    fDirty0->setEmpty();
+    fDirty1->setEmpty();
+}
+
+SkPageFlipper::SkPageFlipper(int width, int height) {
+    fWidth = width;
+    fHeight = height;
+    fDirty0 = &fDirty0Storage;
+    fDirty1 = &fDirty1Storage;
+
+    fDirty0->setRect(0, 0, width, height);
+    fDirty1->setEmpty();
+}
+
+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();
+}
+
+void SkPageFlipper::inval() {
+    fDirty1->setRect(0, 0, fWidth, fHeight);
+}
+
+void SkPageFlipper::inval(const SkIRect& rect) {
+    SkIRect r;
+    r.set(0, 0, fWidth, fHeight);
+    if (r.intersect(rect)) {
+        fDirty1->op(r, SkRegion::kUnion_Op);
+    }
+}
+
+void SkPageFlipper::inval(const SkRegion& rgn) {
+    SkRegion r;
+    r.setRect(0, 0, fWidth, fHeight);
+    if (r.op(rgn, SkRegion::kIntersect_Op)) {
+        fDirty1->op(r, SkRegion::kUnion_Op);
+    }
+}
+
+void SkPageFlipper::inval(const SkRect& rect, bool antialias) {
+    SkIRect r;
+    rect.round(&r);
+    if (antialias) {
+        r.inset(-1, -1);
+    }
+    this->inval(r);
+}
+
+const SkRegion& SkPageFlipper::update(SkRegion* copyBits) {
+    // Copy over anything new from page0 that isn't dirty in page1
+    copyBits->op(*fDirty0, *fDirty1, SkRegion::kDifference_Op);
+    SkTSwap<SkRegion*>(fDirty0, fDirty1);
+    fDirty1->setEmpty();
+    return *fDirty0;
+}
+
+
diff --git a/legacy/src/images/SkScaledBitmapSampler.cpp b/legacy/src/images/SkScaledBitmapSampler.cpp
new file mode 100644
index 0000000..1af433d
--- /dev/null
+++ b/legacy/src/images/SkScaledBitmapSampler.cpp
@@ -0,0 +1,431 @@
+
+/*
+ * Copyright 2007 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 "SkScaledBitmapSampler.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+// 8888
+
+static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32(0xFF, src[0], src[0], src[0]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    unsigned alphaMask = 0xFF;
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+// 565
+
+static bool Sample_Gray_D565(void* SK_RESTRICT dstRow,
+                             const uint8_t* SK_RESTRICT src,
+                             int width, int deltaSrc, int, const SkPMColor[]) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow,
+                               const uint8_t* SK_RESTRICT src,
+                           int width, int deltaSrc, int y, const SkPMColor[]) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    DITHER_565_SCAN(y);
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow,
+                             const uint8_t* SK_RESTRICT src,
+                             int width, int deltaSrc, int, const SkPMColor[]) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_D565_D565(void* SK_RESTRICT dstRow,
+                             const uint8_t* SK_RESTRICT src,
+                             int width, int deltaSrc, int, const SkPMColor[]) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    uint16_t* SK_RESTRICT castedSrc = (uint16_t*) src;
+    for (int x = 0; x < width; x++) {
+        dst[x] = castedSrc[0];
+        castedSrc += deltaSrc >> 1;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow,
+                               const uint8_t* SK_RESTRICT src,
+                           int width, int deltaSrc, int y, const SkPMColor[]) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    DITHER_565_SCAN(y);
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+// 4444
+
+static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    for (int x = 0; x < width; x++) {
+        unsigned gray = src[0] >> 4;
+        dst[x] = SkPackARGB4444(0xF, gray, gray, gray);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int y, const SkPMColor[]) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    DITHER_4444_SCAN(y);
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0],
+                                      DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int y, const SkPMColor[]) {
+    SkPMColor16* dst = (SkPMColor16*)dstRow;
+    DITHER_4444_SCAN(y);
+
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2],
+                                      DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int, const SkPMColor[]) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    unsigned alphaMask = 0xFF;
+
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        dst[x] = SkPixel32ToPixel4444(c);
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int y, const SkPMColor[]) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    unsigned alphaMask = 0xFF;
+    DITHER_4444_SCAN(y);
+
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+// Index
+
+#define A32_MASK_IN_PLACE   (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
+
+static bool Sample_Index_D8888(void* SK_RESTRICT dstRow,
+                               const uint8_t* SK_RESTRICT src,
+                       int width, int deltaSrc, int, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    SkPMColor cc = A32_MASK_IN_PLACE;
+    for (int x = 0; x < width; x++) {
+        SkPMColor c = ctable[*src];
+        cc &= c;
+        dst[x] = c;
+        src += deltaSrc;
+    }
+    return cc != A32_MASK_IN_PLACE;
+}
+
+static bool Sample_Index_D565(void* SK_RESTRICT dstRow,
+                               const uint8_t* SK_RESTRICT src,
+                       int width, int deltaSrc, int, const SkPMColor ctable[]) {
+
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPixel32ToPixel16(ctable[*src]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_Index_D565_D(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src, int width,
+                                int deltaSrc, int y, const SkPMColor ctable[]) {
+
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    DITHER_565_SCAN(y);
+
+    for (int x = 0; x < width; x++) {
+        SkPMColor c = ctable[*src];
+        dst[x] = SkDitherRGBTo565(SkGetPackedR32(c), SkGetPackedG32(c),
+                                  SkGetPackedB32(c), DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_Index_D4444(void* SK_RESTRICT dstRow,
+                               const uint8_t* SK_RESTRICT src, int width,
+                               int deltaSrc, int y, const SkPMColor ctable[]) {
+
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    SkPMColor cc = A32_MASK_IN_PLACE;
+    for (int x = 0; x < width; x++) {
+        SkPMColor c = ctable[*src];
+        cc &= c;
+        dst[x] = SkPixel32ToPixel4444(c);
+        src += deltaSrc;
+    }
+    return cc != A32_MASK_IN_PLACE;
+}
+
+static bool Sample_Index_D4444_D(void* SK_RESTRICT dstRow,
+                                 const uint8_t* SK_RESTRICT src, int width,
+                                int deltaSrc, int y, const SkPMColor ctable[]) {
+
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    SkPMColor cc = A32_MASK_IN_PLACE;
+    DITHER_4444_SCAN(y);
+
+    for (int x = 0; x < width; x++) {
+        SkPMColor c = ctable[*src];
+        cc &= c;
+        dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return cc != A32_MASK_IN_PLACE;
+}
+
+static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
+                            const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int, const SkPMColor[]) {
+    if (1 == deltaSrc) {
+        memcpy(dstRow, src, width);
+    } else {
+        uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow;
+        for (int x = 0; x < width; x++) {
+            dst[x] = src[0];
+            src += deltaSrc;
+        }
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkScaledBitmapSampler.h"
+
+SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
+                                             int sampleSize) {
+    if (width <= 0 || height <= 0) {
+        sk_throw();
+    }
+    
+    if (sampleSize <= 1) {
+        fScaledWidth = width;
+        fScaledHeight = height;
+        fX0 = fY0 = 0;
+        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,
+                                  const SkPMColor ctable[]) {
+    static const RowProc gProcs[] = {
+        // 8888 (no dither distinction)
+        Sample_Gray_D8888,  Sample_Gray_D8888,
+        Sample_RGBx_D8888,  Sample_RGBx_D8888,
+        Sample_RGBA_D8888,  Sample_RGBA_D8888,
+        Sample_Index_D8888, Sample_Index_D8888,
+        NULL,               NULL,
+        // 565 (no alpha distinction)
+        Sample_Gray_D565,   Sample_Gray_D565_D,
+        Sample_RGBx_D565,   Sample_RGBx_D565_D,
+        Sample_RGBx_D565,   Sample_RGBx_D565_D,
+        Sample_Index_D565,  Sample_Index_D565_D,
+        Sample_D565_D565,   Sample_D565_D565,
+        // 4444
+        Sample_Gray_D4444,  Sample_Gray_D4444_D,
+        Sample_RGBx_D4444,  Sample_RGBx_D4444_D,
+        Sample_RGBA_D4444,  Sample_RGBA_D4444_D,
+        Sample_Index_D4444, Sample_Index_D4444_D,
+        NULL,               NULL,
+        // Index8
+        NULL,               NULL,
+        NULL,               NULL,
+        NULL,               NULL,
+        Sample_Index_DI,    Sample_Index_DI,
+        NULL,               NULL,
+    };
+
+    fCTable = ctable;
+
+    int index = 0;
+    if (dither) {
+        index += 1;
+    }
+    switch (sc) {
+        case SkScaledBitmapSampler::kGray:
+            fSrcPixelSize = 1;
+            index += 0;
+            break;
+        case SkScaledBitmapSampler::kRGB:
+            fSrcPixelSize = 3;
+            index += 2;
+            break;
+        case SkScaledBitmapSampler::kRGBX:
+            fSrcPixelSize = 4;
+            index += 2;
+            break;
+        case SkScaledBitmapSampler::kRGBA:
+            fSrcPixelSize = 4;
+            index += 4;
+            break;
+        case SkScaledBitmapSampler::kIndex:
+            fSrcPixelSize = 1;
+            index += 6;
+            break;
+        case SkScaledBitmapSampler::kRGB_565:
+            fSrcPixelSize = 2;
+            index += 8;
+            break;
+        default:
+            return false;
+    }
+
+    switch (dst->config()) {
+        case SkBitmap::kARGB_8888_Config:
+            index += 0;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            index += 10;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            index += 20;
+            break;
+        case SkBitmap::kIndex8_Config:
+            index += 30;
+            break;
+        default:
+            return false;
+    }
+    
+    fRowProc = gProcs[index];
+    fDstRow = (char*)dst->getPixels();
+    fDstRowBytes = dst->rowBytes();
+    fCurrY = 0;
+    return fRowProc != NULL;
+}
+
+bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
+    SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
+
+    bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
+                             fDX * fSrcPixelSize, fCurrY, fCTable);
+    fDstRow += fDstRowBytes;
+    fCurrY += 1;
+    return hadAlpha;
+}
diff --git a/legacy/src/images/SkScaledBitmapSampler.h b/legacy/src/images/SkScaledBitmapSampler.h
new file mode 100644
index 0000000..8a735df
--- /dev/null
+++ b/legacy/src/images/SkScaledBitmapSampler.h
@@ -0,0 +1,69 @@
+
+/*
+ * 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 SkScaledBitmapSampler_DEFINED
+#define SkScaledBitmapSampler_DEFINED
+
+#include "SkTypes.h"
+#include "SkColor.h"
+
+class SkBitmap;
+
+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; }
+
+    enum SrcConfig {
+        kGray,  // 1 byte per pixel
+        kIndex, // 1 byte per pixel
+        kRGB,   // 3 bytes per pixel
+        kRGBX,  // 4 byes per pixel (ignore 4th)
+        kRGBA,  // 4 bytes per pixel
+        kRGB_565 // 2 bytes per pixel
+    };
+
+    // Given a dst bitmap (with pixels already allocated) and a src-config,
+    // prepares iterator to process the src colors and write them into dst.
+    // Returns false if the request cannot be fulfulled.
+    bool begin(SkBitmap* dst, SrcConfig sc, bool doDither,
+               const SkPMColor* = NULL);
+    // call with row of src pixels, for y = 0...scaledHeight-1.
+    // returns true if the row had non-opaque alpha in it
+    bool next(const uint8_t* SK_RESTRICT src);
+
+private:
+    int fScaledWidth;
+    int fScaledHeight;
+
+    int fX0;    // first X coord to sample
+    int fY0;    // first Y coord (scanline) to sample
+    int fDX;    // step between X samples
+    int fDY;    // step between Y samples
+
+    typedef bool (*RowProc)(void* SK_RESTRICT dstRow,
+                            const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int y,
+                            const SkPMColor[]);
+
+    // setup state
+    char*   fDstRow; // points into bitmap's pixels
+    int     fDstRowBytes;
+    int     fCurrY; // used for dithering
+    int     fSrcPixelSize;  // 1, 3, 4    
+    RowProc fRowProc;
+
+    // optional reference to the src colors if the src is a palette model
+    const SkPMColor* fCTable;
+};
+
+#endif
diff --git a/legacy/src/images/bmpdecoderhelper.cpp b/legacy/src/images/bmpdecoderhelper.cpp
new file mode 100644
index 0000000..7749664
--- /dev/null
+++ b/legacy/src/images/bmpdecoderhelper.cpp
@@ -0,0 +1,369 @@
+
+/*
+ * Copyright 2007 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.
+ */
+
+// Author: cevans@google.com (Chris Evans)
+
+#include "bmpdecoderhelper.h"
+
+namespace image_codec {
+
+static const int kBmpHeaderSize = 14;
+static const int kBmpInfoSize = 40;
+static const int kBmpOS2InfoSize = 12;
+static const int kMaxDim = SHRT_MAX / 2;
+
+bool BmpDecoderHelper::DecodeImage(const char* p,
+                                   int len,
+                                   int max_pixels,
+                                   BmpDecoderCallback* callback) {
+  data_ = reinterpret_cast<const uint8*>(p);
+  pos_ = 0;
+  len_ = len;
+  inverted_ = true;
+  // Parse the header structure.
+  if (len < kBmpHeaderSize + 4) {
+    return false;
+  }
+  GetShort();  // Signature.
+  GetInt();  // Size.
+  GetInt();  // Reserved.
+  int offset = GetInt();
+  // Parse the info structure.
+  int infoSize = GetInt();
+  if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) {
+    return false;
+  }
+  int cols = 0;
+  int comp = 0;
+  int colLen = 4;
+  if (infoSize >= kBmpInfoSize) {
+    if (len < kBmpHeaderSize + kBmpInfoSize) {
+      return false;
+    }
+    width_ = GetInt();
+    height_ = GetInt();
+    GetShort();  // Planes.
+    bpp_ = GetShort();
+    comp = GetInt();
+    GetInt();  // Size.
+    GetInt();  // XPPM.
+    GetInt();  // YPPM.
+    cols = GetInt();
+    GetInt();  // Important colours.
+  } else {
+    if (len < kBmpHeaderSize + kBmpOS2InfoSize) {
+      return false;
+    }
+    colLen = 3;
+    width_ = GetShort();
+    height_ = GetShort();
+    GetShort();  // Planes.
+    bpp_ = GetShort();
+  }
+  if (height_ < 0) {
+    height_ = -height_;
+    inverted_ = false;
+  }
+  if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) {
+    return false;
+  }
+  if (width_ * height_ > max_pixels) {
+    return false;
+  }
+  if (cols < 0 || cols > 256) {
+    return false;
+  }
+  // Allocate then read in the colour map.
+  if (cols == 0 && bpp_ <= 8) {
+    cols = 1 << bpp_;
+  }
+  if (bpp_ <= 8 || cols > 0) {
+    uint8* colBuf = new uint8[256 * 3];
+    memset(colBuf, '\0', 256 * 3);
+    colTab_.reset(colBuf);
+  }
+  if (cols > 0) {
+    if (pos_ + (cols * colLen) > len_) {
+      return false;
+    }
+    for (int i = 0; i < cols; ++i) {
+      int base = i * 3;
+      colTab_[base + 2] = GetByte();
+      colTab_[base + 1] = GetByte();
+      colTab_[base] = GetByte();
+      if (colLen == 4) {
+        GetByte();
+      }
+    }
+  }
+  // Read in the compression data if necessary.
+  redBits_ = 0x7c00;
+  greenBits_ = 0x03e0;
+  blueBits_ = 0x001f;
+  bool rle = false;
+  if (comp == 1 || comp == 2) {
+    rle = true;
+  } else if (comp == 3) {
+    if (pos_ + 12 > len_) {
+      return false;
+    }
+    redBits_ = GetInt() & 0xffff;
+    greenBits_ = GetInt() & 0xffff;
+    blueBits_ = GetInt() & 0xffff;
+  }
+  redShiftRight_ = CalcShiftRight(redBits_);
+  greenShiftRight_ = CalcShiftRight(greenBits_);
+  blueShiftRight_ = CalcShiftRight(blueBits_);
+  redShiftLeft_ = CalcShiftLeft(redBits_);
+  greenShiftLeft_ = CalcShiftLeft(greenBits_);
+  blueShiftLeft_ = CalcShiftLeft(blueBits_);
+  rowPad_ = 0;
+  pixelPad_ = 0;
+  int rowLen;
+  if (bpp_ == 32) {
+    rowLen = width_ * 4;
+    pixelPad_ = 1;
+  } else if (bpp_ == 24) {
+    rowLen = width_ * 3;
+  } else if (bpp_ == 16) {
+    rowLen = width_ * 2;
+  } else if (bpp_ == 8) {
+    rowLen = width_;
+  } else if (bpp_ == 4) {
+    rowLen = width_ / 2;
+    if (width_ & 1) {
+      rowLen++;
+    }
+  } else if (bpp_ == 1) {
+    rowLen = width_ / 8;
+    if (width_ & 7) {
+      rowLen++;
+    }
+  } else {
+    return false;
+  }
+  // Round the rowLen up to a multiple of 4.
+  if (rowLen % 4 != 0) {
+    rowPad_ = 4 - (rowLen % 4);
+    rowLen += rowPad_;
+  }
+
+  if (offset > 0 && offset > pos_ && offset < len_) {
+    pos_ = offset;
+  }
+  // Deliberately off-by-one; a load of BMPs seem to have their last byte
+  // missing.
+  if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) {
+    return false;
+  }
+
+  output_ = callback->SetSize(width_, height_);
+  if (NULL == output_) {
+    return true;  // meaning we succeeded, but they want us to stop now
+  }
+
+  if (rle && (bpp_ == 4 || bpp_ == 8)) {
+    DoRLEDecode();
+  } else {
+    DoStandardDecode();
+  }
+  return true;
+}
+
+void BmpDecoderHelper::DoRLEDecode() {
+  static const uint8 RLE_ESCAPE = 0;
+  static const uint8 RLE_EOL = 0;
+  static const uint8 RLE_EOF = 1;
+  static const uint8 RLE_DELTA = 2;
+  int x = 0;
+  int y = height_ - 1;
+  while (pos_ < len_ - 1) {
+    uint8 cmd = GetByte();
+    if (cmd != RLE_ESCAPE) {
+      uint8 pixels = GetByte();
+      int num = 0;
+      uint8 col = pixels;
+      while (cmd-- && x < width_) {
+        if (bpp_ == 4) {
+          if (num & 1) {
+            col = pixels & 0xf;
+          } else {
+            col = pixels >> 4;
+          }
+        }
+        PutPixel(x++, y, col);
+        num++;
+      }
+    } else {
+      cmd = GetByte();
+      if (cmd == RLE_EOF) {
+        return;
+      } else if (cmd == RLE_EOL) {
+        x = 0;
+        y--;
+        if (y < 0) {
+          return;
+        }
+      } else if (cmd == RLE_DELTA) {
+        if (pos_ < len_ - 1) {
+          uint8 dx = GetByte();
+          uint8 dy = GetByte();
+          x += dx;
+          if (x > width_) {
+            x = width_;
+          }
+          y -= dy;
+          if (y < 0) {
+            return;
+          }
+        }
+      } else {
+        int num = 0;
+        int bytesRead = 0;
+        uint8 val = 0;
+        while (cmd-- && pos_ < len_) {
+          if (bpp_ == 8 || !(num & 1)) {
+            val = GetByte();
+            bytesRead++;
+          }
+          uint8 col = val;
+          if (bpp_ == 4) {
+            if (num & 1) {
+              col = col & 0xf;
+            } else {
+              col >>= 4;
+            }
+          }
+          if (x < width_) {
+            PutPixel(x++, y, col);
+          }
+          num++;
+        }
+        // All pixel runs must be an even number of bytes - skip a byte if we
+        // read an odd number.
+        if ((bytesRead & 1) && pos_ < len_) {
+          GetByte();
+        }
+      }
+    }
+  }
+}
+
+void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) {
+  CHECK(x >= 0 && x < width_);
+  CHECK(y >= 0 && y < height_);
+  if (!inverted_) {
+    y = height_ - (y + 1);
+  }
+
+  int base = ((y * width_) + x) * 3;
+  int colBase = col * 3;
+  output_[base] = colTab_[colBase];
+  output_[base + 1] = colTab_[colBase + 1];
+  output_[base + 2] = colTab_[colBase + 2];
+}
+
+void BmpDecoderHelper::DoStandardDecode() {
+  int row = 0;
+  uint8 currVal = 0;
+  for (int h = height_ - 1; h >= 0; h--, row++) {
+    int realH = h;
+    if (!inverted_) {
+      realH = height_ - (h + 1);
+    }
+    uint8* line = output_ + (3 * width_ * realH);
+    for (int w = 0; w < width_; w++) {
+      if (bpp_ >= 24) {
+        line[2] = GetByte();
+        line[1] = GetByte();
+        line[0] = GetByte();
+      } else if (bpp_ == 16) {
+        uint32 val = GetShort();
+        line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_;
+        line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_;
+        line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_;
+      } else if (bpp_ <= 8) {
+        uint8 col;
+        if (bpp_ == 8) {
+          col = GetByte();
+        } else if (bpp_ == 4) {
+          if ((w % 2) == 0) {
+            currVal = GetByte();
+            col = currVal >> 4;
+          } else {
+            col = currVal & 0xf;
+          }
+        } else {
+          if ((w % 8) == 0) {
+            currVal = GetByte();
+          }
+          int bit = w & 7;
+          col = ((currVal >> (7 - bit)) & 1);
+        }
+        int base = col * 3;
+        line[0] = colTab_[base];
+        line[1] = colTab_[base + 1];
+        line[2] = colTab_[base + 2];
+      }
+      line += 3;
+      for (int i = 0; i < pixelPad_; ++i) {
+        GetByte();
+      }
+    }
+    for (int i = 0; i < rowPad_; ++i) {
+      GetByte();
+    }
+  }
+}
+
+int BmpDecoderHelper::GetInt() {
+  uint8 b1 = GetByte();
+  uint8 b2 = GetByte();
+  uint8 b3 = GetByte();
+  uint8 b4 = GetByte();
+  return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+}
+
+int BmpDecoderHelper::GetShort() {
+  uint8 b1 = GetByte();
+  uint8 b2 = GetByte();
+  return b1 | (b2 << 8);
+}
+
+uint8 BmpDecoderHelper::GetByte() {
+  CHECK(pos_ >= 0 && pos_ <= len_);
+  // We deliberately allow this off-by-one access to cater for BMPs with their
+  // last byte missing.
+  if (pos_ == len_) {
+    return 0;
+  }
+  return data_[pos_++];
+}
+
+int BmpDecoderHelper::CalcShiftRight(uint32 mask) {
+  int ret = 0;
+  while (mask != 0 && !(mask & 1)) {
+    mask >>= 1;
+    ret++;
+  }
+  return ret;
+}
+
+int BmpDecoderHelper::CalcShiftLeft(uint32 mask) {
+  int ret = 0;
+  while (mask != 0 && !(mask & 1)) {
+    mask >>= 1;
+  }
+  while (mask != 0 && !(mask & 0x80)) {
+    mask <<= 1;
+    ret++;
+  }
+  return ret;
+}
+
+}  // namespace image_codec
diff --git a/legacy/src/images/bmpdecoderhelper.h b/legacy/src/images/bmpdecoderhelper.h
new file mode 100644
index 0000000..f3324ee
--- /dev/null
+++ b/legacy/src/images/bmpdecoderhelper.h
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright 2007 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 IMAGE_CODEC_BMPDECODERHELPER_H__
+#define IMAGE_CODEC_BMPDECODERHELPER_H__
+
+///////////////////////////////////////////////////////////////////////////////
+// this section is my current "glue" between google3 code and android.
+// will be fixed soon
+
+#include "SkTypes.h"
+#include <limits.h>
+#define DISALLOW_EVIL_CONSTRUCTORS(name)
+#define CHECK(predicate)  SkASSERT(predicate)
+typedef uint8_t uint8;
+typedef uint32_t uint32;
+
+template <typename T> class scoped_array {
+private:
+  T* ptr_;
+  scoped_array(scoped_array const&);
+  scoped_array& operator=(const scoped_array&);
+
+public:
+  explicit scoped_array(T* p = 0) : ptr_(p) {}
+  ~scoped_array() {
+    delete[] ptr_;
+  }
+  
+  void reset(T* p = 0) {
+    if (p != ptr_) {
+      delete[] ptr_;
+      ptr_ = p;
+    }
+  }
+  
+  T& operator[](int i) const {
+    return ptr_[i];
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace image_codec {
+
+class BmpDecoderCallback {
+ 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
+   * all of the resulting pixels (widht * height * 3 bytes). If it returns NULL,
+   * then the decoder will abort, but return true, as the caller has received
+   * valid dimensions.
+   */
+  virtual uint8* SetSize(int width, int height) = 0;
+   
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderCallback);
+};
+
+class BmpDecoderHelper {
+ public:
+  BmpDecoderHelper() { }
+  ~BmpDecoderHelper() { }
+  bool DecodeImage(const char* data,
+                   int len,
+                   int max_pixels,
+                   BmpDecoderCallback* callback);
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderHelper);
+
+  void DoRLEDecode();
+  void DoStandardDecode();
+  void PutPixel(int x, int y, uint8 col);
+
+  int GetInt();
+  int GetShort();
+  uint8 GetByte();
+  int CalcShiftRight(uint32 mask);
+  int CalcShiftLeft(uint32 mask);
+
+  const uint8* data_;
+  int pos_;
+  int len_;
+  int width_;
+  int height_;
+  int bpp_;
+  int pixelPad_;
+  int rowPad_;
+  scoped_array<uint8> colTab_;
+  uint32 redBits_;
+  uint32 greenBits_;
+  uint32 blueBits_;
+  int redShiftRight_;
+  int greenShiftRight_;
+  int blueShiftRight_;
+  int redShiftLeft_;
+  int greenShiftLeft_;
+  int blueShiftLeft_;
+  uint8* output_;
+  bool inverted_;
+};
+  
+} // namespace
+
+#endif
diff --git a/legacy/src/opts/SkBitmapProcState_opts_SSE2.cpp b/legacy/src/opts/SkBitmapProcState_opts_SSE2.cpp
new file mode 100644
index 0000000..1852c66
--- /dev/null
+++ b/legacy/src/opts/SkBitmapProcState_opts_SSE2.cpp
@@ -0,0 +1,634 @@
+
+/*
+ * 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.
+ */
+
+
+#include <emmintrin.h>
+#include "SkBitmapProcState_opts_SSE2.h"
+#include "SkUtils.h"
+
+void S32_opaque_D32_filter_DX_SSE2(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);
+    SkASSERT(s.fAlphaScale == 256);
+
+    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.
+        *colors++ = _mm_cvtsi128_si32(sum);
+    } while (--count > 0);
+}
+
+void S32_alpha_D32_filter_DX_SSE2(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);
+    SkASSERT(s.fAlphaScale < 256);
+
+    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();
+
+    // ( alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha )
+    __m128i alpha = _mm_set1_epi16(s.fAlphaScale);
+
+    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);
+
+        // Multiply by alpha.
+        sum = _mm_mullo_epi16(sum, alpha);
+
+        // 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.
+        *colors++ = _mm_cvtsi128_si32(sum);
+    } while (--count > 0);
+}
+
+static inline uint32_t ClampX_ClampY_pack_filter(SkFixed f, unsigned max,
+                                                 SkFixed one) {
+    unsigned i = SkClampMax(f >> 16, max);
+    i = (i << 4) | ((f >> 12) & 0xF);
+    return (i << 14) | SkClampMax((f + one) >> 16, max);
+}
+
+/*  SSE version of ClampX_ClampY_filter_scale()
+ *  portable version is in core/SkBitmapProcState_matrix.h
+ */
+void ClampX_ClampY_filter_scale_SSE2(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);
+    
+    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++ = ClampX_ClampY_pack_filter(fy, maxY, s.fFilterOneY);
+    // now initialize fx
+    fx = SkScalarToFixed(pt.fX) - (one >> 1);
+
+    // 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) {
+        if (count >= 4) {
+            // SSE version of decal_filter_scale
+            while ((size_t(xy) & 0x0F) != 0) {
+                SkASSERT((fx >> (16 + 14)) == 0);
+                *xy++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+                fx += dx;
+                count--;
+            }
+
+            __m128i wide_1    = _mm_set1_epi32(1);
+            __m128i wide_dx4  = _mm_set1_epi32(dx * 4);
+            __m128i wide_fx   = _mm_set_epi32(fx + dx * 3, fx + dx * 2,
+                                              fx + dx, fx);
+
+            while (count >= 4) {
+                __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_store_si128(reinterpret_cast<__m128i*>(xy), wide_out);
+      
+                xy += 4;
+                fx += dx * 4;
+                wide_fx  = _mm_add_epi32(wide_fx, wide_dx4);
+                count -= 4;
+            } // while count >= 4
+        } // if count >= 4
+
+        while (count-- > 0) {
+            SkASSERT((fx >> (16 + 14)) == 0);
+            *xy++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+            fx += dx;
+        }
+    } 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 
+        // than max 16bit interger in the real world.
+        if ((count >= 4) && (maxX <= 0xFFFF)) {
+            while (((size_t)xy & 0x0F) != 0) {
+                *xy++ = ClampX_ClampY_pack_filter(fx, maxX, one);
+                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_mask = _mm_set1_epi32(0xF);
+
+             while (count >= 4) {
+                __m128i wide_i;
+                __m128i wide_lo;
+                __m128i wide_fx1;
+
+                // i = SkClampMax(f>>16,maxX)
+                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);     
+    
+                // 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), 
+                                                        _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); 
+    
+                wide_fx = _mm_add_epi32(wide_fx, wide_dx4);
+                fx += dx * 4;   
+                xy += 4;
+                count -= 4;
+            } // while count >= 4
+        } // if count >= 4
+
+        while (count-- > 0) {
+            *xy++ = ClampX_ClampY_pack_filter(fx, maxX, one);
+            fx += dx;
+        }
+    }
+}
+
+/*  SSE version of ClampX_ClampY_nofilter_scale()
+ *  portable version is in core/SkBitmapProcState_matrix.h
+ */
+void ClampX_ClampY_nofilter_scale_SSE2(const SkBitmapProcState& s,
+                                    uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+
+    // 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++ = 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));
+        return;
+    }
+
+    const SkFixed dx = s.fInvSx;
+
+    // test if we don't need to apply the tile proc
+    if ((unsigned)(fx >> 16) <= maxX &&
+        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
+        // SSE version of decal_nofilter_scale
+        if (count >= 8) {
+            while (((size_t)xy & 0x0F) != 0) {
+                *xy++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
+                fx += 2 * dx;
+                count -= 2;
+            }
+
+            __m128i wide_dx4 = _mm_set1_epi32(dx * 4);
+            __m128i wide_dx8 = _mm_add_epi32(wide_dx4, wide_dx4);
+
+            __m128i wide_low = _mm_set_epi32(fx + dx * 3, fx + dx * 2,
+                                             fx + dx, fx);
+            __m128i wide_high = _mm_add_epi32(wide_low, wide_dx4);
+
+            while (count >= 8) {
+                __m128i wide_out_low = _mm_srli_epi32(wide_low, 16);
+                __m128i wide_out_high = _mm_srli_epi32(wide_high, 16);
+
+                __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);
+
+                xy += 4;
+                fx += dx * 8;
+                count -= 8;
+            }
+        } // if count >= 8
+
+        uint16_t* xx = reinterpret_cast<uint16_t*>(xy);
+        while (count-- > 0) {
+            *xx++ = SkToU16(fx >> 16);
+            fx += dx;
+        }
+    } 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 
+        // 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);
+                fx += 2 * dx;
+                count -= 2;
+            }
+
+            __m128i wide_dx4 = _mm_set1_epi32(dx * 4);
+            __m128i wide_dx8 = _mm_add_epi32(wide_dx4, wide_dx4);
+
+            __m128i wide_low = _mm_set_epi32(fx + dx * 3, fx + dx * 2,
+                                             fx + dx, fx);
+            __m128i wide_high = _mm_add_epi32(wide_low, wide_dx4);
+            __m128i wide_maxX = _mm_set1_epi32(maxX);
+
+            while (count >= 8) {
+                __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, 
+                                              _mm_setzero_si128());
+                wide_out_low  = _mm_min_epi16(wide_out_low, wide_maxX);
+                wide_out_high = _mm_max_epi16(wide_out_high,
+                                              _mm_setzero_si128());
+                wide_out_high = _mm_min_epi16(wide_out_high, wide_maxX);
+
+                __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);
+
+                xy += 4;
+                fx += dx * 8;
+                count -= 8;
+            }
+        } // if count >= 8
+
+        uint16_t* xx = reinterpret_cast<uint16_t*>(xy);
+        while (count-- > 0) {
+            *xx++ = SkClampMax(fx >> 16, maxX);
+            fx += dx;
+        }
+    }
+}
+
+/*  SSE version of ClampX_ClampY_filter_affine()
+ *  portable version is in core/SkBitmapProcState_matrix.h
+ */
+void ClampX_ClampY_filter_affine_SSE2(const SkBitmapProcState& s,
+                                      uint32_t xy[], int count, int x, int y) {
+    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 >= 2 && (maxX <= 0xFFFF)) {
+        SkFixed dx2 = dx + dx;
+        SkFixed dy2 = dy + dy;
+
+        __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_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), 
+                                           _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);     
+    
+            // 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), 
+                                                   _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); 
+    
+            wide_f = _mm_add_epi32(wide_f, wide_d2);
+
+            fx += dx2; 
+            fy += dy2;
+            xy += 4;
+            count -= 2;
+        } // while count >= 2
+    } // if count >= 2
+
+    while (count-- > 0) {
+        *xy++ = ClampX_ClampY_pack_filter(fy, maxY, oneY);
+        fy += dy;
+        *xy++ = ClampX_ClampY_pack_filter(fx, maxX, oneX);
+        fx += dx;          
+    }
+}
+
+/*  SSE version of ClampX_ClampY_nofilter_affine()
+ *  portable version is in core/SkBitmapProcState_matrix.h
+ */
+void ClampX_ClampY_nofilter_affine_SSE2(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);
+
+    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 (count >= 4 && (maxX <= 0xFFFF)) {
+        while (((size_t)xy & 0x0F) != 0) {
+            *xy++ = (SkClampMax(fy >> 16, maxY) << 16) | 
+                                  SkClampMax(fx >> 16, maxX);
+            fx += dx;
+            fy += dy;
+            count--;
+        }
+
+        SkFixed dx4 = dx * 4;
+        SkFixed dy4 = dy * 4;
+
+        __m128i wide_fx   = _mm_set_epi32(fx + dx * 3, fx + dx * 2,
+                                          fx + dx, fx);
+        __m128i wide_fy   = _mm_set_epi32(fy + dy * 3, fy + dy * 2,
+                                          fy + dy, fy);
+        __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); 
+
+        while (count >= 4) {
+            // SkClampMax(fx>>16,maxX)
+            __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), 
+                                            _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); 
+ 
+            wide_fx = _mm_add_epi32(wide_fx, wide_dx4);
+            wide_fy = _mm_add_epi32(wide_fy, wide_dy4);
+
+            fx += dx4; 
+            fy += dy4;
+            xy += 4;
+            count -= 4;
+        } // while count >= 4
+    } // if count >= 4
+
+    while (count-- > 0) {
+        *xy++ = (SkClampMax(fy >> 16, maxY) << 16) |
+                              SkClampMax(fx >> 16, maxX);
+        fx += dx;
+        fy += dy;           
+    }
+}
diff --git a/legacy/src/opts/SkBitmapProcState_opts_SSE2.h b/legacy/src/opts/SkBitmapProcState_opts_SSE2.h
new file mode 100644
index 0000000..3fdf696
--- /dev/null
+++ b/legacy/src/opts/SkBitmapProcState_opts_SSE2.h
@@ -0,0 +1,27 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkBitmapProcState.h"
+
+void S32_opaque_D32_filter_DX_SSE2(const SkBitmapProcState& s,
+                                   const uint32_t* xy,
+                                   int count, uint32_t* colors);
+void S32_alpha_D32_filter_DX_SSE2(const SkBitmapProcState& s,
+                                  const uint32_t* xy,
+                                  int count, uint32_t* colors);
+void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count,
+                  SkPMColor color);
+void ClampX_ClampY_filter_scale_SSE2(const SkBitmapProcState& s, uint32_t xy[],
+                                     int count, int x, int y);
+void ClampX_ClampY_nofilter_scale_SSE2(const SkBitmapProcState& s,
+                                       uint32_t xy[], int count, int x, int y);
+void ClampX_ClampY_filter_affine_SSE2(const SkBitmapProcState& s,
+                                      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);
diff --git a/legacy/src/opts/SkBitmapProcState_opts_SSSE3.cpp b/legacy/src/opts/SkBitmapProcState_opts_SSSE3.cpp
new file mode 100644
index 0000000..98b3445
--- /dev/null
+++ b/legacy/src/opts/SkBitmapProcState_opts_SSSE3.cpp
@@ -0,0 +1,497 @@
+/*
+ * 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 <tmmintrin.h>  // SSSE3
+#include "SkBitmapProcState_opts_SSSE3.h"
+#include "SkUtils.h"
+
+// adding anonymous namespace seemed to force gcc to inline directly the
+// instantiation, instead of creating the functions
+// S32_generic_D32_filter_DX_SSSE3<true> and
+// S32_generic_D32_filter_DX_SSSE3<false> which were then called by the
+// external functions.
+namespace {
+// In this file, variations for alpha and non alpha versions are implemented
+// with a template, as it makes the code more compact and a bit easier to
+// maintain, while making the compiler generate the same exact code as with
+// two functions that only differ by a few lines.
+
+
+// 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_DX 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_x_result vector of 8 bit components that will contain the
+//              (4x(x3), 4x(x2), 4x(x1), 4x(x0)) upon return.
+// @param sixteen_minus_x vector of 8 bit components, containing
+//              (4x(16 - x3), 4x(16 - x2), 4x(16 - x1), 4x(16 - x0))
+inline void PrepareConstantsTwoPixelPairs(const uint32_t* xy,
+                                          const __m128i& mask_3FFF,
+                                          const __m128i& mask_000F,
+                                          const __m128i& sixteen_8bit,
+                                          const __m128i& mask_dist_select,
+                                          __m128i* all_x_result,
+                                          __m128i* sixteen_minus_x,
+                                          int* x0,
+                                          int* x1) {
+    const __m128i xx = _mm_loadu_si128(reinterpret_cast<const __m128i *>(xy));
+
+    // 4 delta X
+    // (x03, x02, x01, x00)
+    const __m128i x0_wide = _mm_srli_epi32(xx, 18);
+    // (x13, x12, x11, x10)
+    const __m128i x1_wide = _mm_and_si128(xx, mask_3FFF);
+
+    _mm_storeu_si128(reinterpret_cast<__m128i *>(x0), x0_wide);
+    _mm_storeu_si128(reinterpret_cast<__m128i *>(x1), x1_wide);
+
+    __m128i all_x = _mm_and_si128(_mm_srli_epi32(xx, 14), mask_000F);
+
+    // (4x(x3), 4x(x2), 4x(x1), 4x(x0))
+    all_x = _mm_shuffle_epi8(all_x, mask_dist_select);
+
+    *all_x_result = all_x;
+    // (4x(16-x3), 4x(16-x2), 4x(16-x1), 4x(16-x0))
+    *sixteen_minus_x = _mm_sub_epi8(sixteen_8bit, all_x);
+}
+
+// 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
+//                will contain (4x(x1, 16-x1), 4x(x0, 16-x0))
+//                or (4x(x3, 16-x3), 4x(x2, 16-x2))
+// @return a vector of 16 bit components containing:
+// (Aa2 * (16 - x1) + Aa3 * x1, ... , Ra0 * (16 - x0) + Ra1 * x0)
+inline __m128i ProcessPixelPairHelper(uint32_t pixel0,
+                                      uint32_t pixel1,
+                                      uint32_t pixel2,
+                                      uint32_t pixel3,
+                                      const __m128i& scale_x) {
+    __m128i a0, a1, a2, a3;
+    // Load 2 pairs of pixels
+    a0 = _mm_cvtsi32_si128(pixel0);
+    a1 = _mm_cvtsi32_si128(pixel1);
+
+    // Interleave pixels.
+    // (0, 0, 0, 0, 0, 0, 0, 0, Aa1, Aa0, Ba1, Ba0, Ga1, Ga0, Ra1, Ra0)
+    a0 = _mm_unpacklo_epi8(a0, a1);
+
+    a2 = _mm_cvtsi32_si128(pixel2);
+    a3 = _mm_cvtsi32_si128(pixel3);
+    // (0, 0, 0, 0, 0, 0, 0, 0, Aa3, Aa2, Ba3, Ba2, Ga3, Ga2, Ra3, Ra2)
+    a2 = _mm_unpacklo_epi8(a2, a3);
+
+    // two pairs of pixel pairs, interleaved.
+    // (Aa3, Aa2, Ba3, Ba2, Ga3, Ga2, Ra3, Ra2,
+    //  Aa1, Aa0, Ba1, Ba0, Ga1, Ga0, Ra1, Ra0)
+    a0 = _mm_unpacklo_epi64(a0, a2);
+
+    // multiply and sum to 16 bit components.
+    // (Aa2 * (16 - x1) + Aa3 * x1, ... , Ra0 * (16 - x0) + Ra1 * x0)
+    // At that point, we use up a bit less than 12 bits for each 16 bit
+    // component:
+    // All components are less than 255. So,
+    // C0 * (16 - x) + C1 * x <= 255 * (16 - x) + 255 * x = 255 * 16.
+    return _mm_maddubs_epi16(a0, scale_x);
+}
+
+// Scale back the results after multiplications to the [0:255] range, and scale
+// by alpha when has_alpha is true.
+// Depending on whether one set or two sets of multiplications had been applied,
+// the results have to be shifted by four places (dividing by 16), or shifted
+// by eight places (dividing by 256), since each multiplication is by a quantity
+// in the range [0:16].
+template<bool has_alpha, int scale>
+inline __m128i ScaleFourPixels(__m128i* pixels,
+                               const __m128i& alpha) {
+    // Divide each 16 bit component by 16 (or 256 depending on scale).
+    *pixels = _mm_srli_epi16(*pixels, scale);
+
+    if (has_alpha) {
+        // Multiply by alpha.
+        *pixels = _mm_mullo_epi16(*pixels, alpha);
+
+        // Divide each 16 bit component by 256.
+        *pixels = _mm_srli_epi16(*pixels, 8);
+    }
+    return *pixels;
+}
+
+// Wrapper to calculate two output pixels from four input pixels. The
+// arguments are the same as ProcessPixelPairHelper. Technically, there are
+// eight input pixels, but since sub_y == 0, the factors applied to half of the
+// pixels is zero (sub_y), and are therefore omitted here to save on some
+// processing.
+// @param alpha when has_alpha is true, scale all resulting components by this
+//              value.
+// @return a vector of 16 bit components containing:
+// ((Aa2 * (16 - x1) + Aa3 * x1) * alpha, ...,
+// (Ra0 * (16 - x0) + Ra1 * x0) * alpha) (when has_alpha is true)
+// otherwise
+// (Aa2 * (16 - x1) + Aa3 * x1, ... , Ra0 * (16 - x0) + Ra1 * x0)
+// In both cases, the results are renormalized (divided by 16) to match the
+// expected formats when storing back the results into memory.
+template<bool has_alpha>
+inline __m128i ProcessPixelPairZeroSubY(uint32_t pixel0,
+                                        uint32_t pixel1,
+                                        uint32_t pixel2,
+                                        uint32_t pixel3,
+                                        const __m128i& scale_x,
+                                        const __m128i& alpha) {
+    __m128i sum = ProcessPixelPairHelper(pixel0, pixel1, pixel2, pixel3,
+                                         scale_x);
+    return ScaleFourPixels<has_alpha, 4>(&sum, alpha);
+}
+
+// Same as ProcessPixelPairZeroSubY, expect processing one output pixel at a
+// time instead of two. As in the above function, only two pixels are needed
+// to generate a single pixel since sub_y == 0.
+// @return same as ProcessPixelPairZeroSubY, except that only the bottom 4
+// 16 bit components are set.
+template<bool has_alpha>
+inline __m128i ProcessOnePixelZeroSubY(uint32_t pixel0,
+                                       uint32_t pixel1,
+                                       __m128i scale_x,
+                                       __m128i alpha) {
+    __m128i a0 = _mm_cvtsi32_si128(pixel0);
+    __m128i a1 = _mm_cvtsi32_si128(pixel1);
+
+    // Interleave
+    a0 = _mm_unpacklo_epi8(a0, a1);
+
+    // (a0 * (16-x) + a1 * x)
+    __m128i sum = _mm_maddubs_epi16(a0, scale_x);
+
+    return ScaleFourPixels<has_alpha, 4>(&sum, alpha);
+}
+
+// Methods when sub_y != 0
+
+
+// Same as ProcessPixelPairHelper, except that the values are scaled by y.
+// @param y vector of 16 bit components containing 'y' values. There are two
+//        cases in practice, where y will contain the sub_y constant, or will
+//        contain the 16 - sub_y constant.
+// @return vector of 16 bit components containing:
+// (y * (Aa2 * (16 - x1) + Aa3 * x1), ... , y * (Ra0 * (16 - x0) + Ra1 * x0))
+inline __m128i ProcessPixelPair(uint32_t pixel0,
+                                uint32_t pixel1,
+                                uint32_t pixel2,
+                                uint32_t pixel3,
+                                const __m128i& scale_x,
+                                const __m128i& y) {
+    __m128i sum = ProcessPixelPairHelper(pixel0, pixel1, pixel2, pixel3,
+                                         scale_x);
+
+    // first row times 16-y or y depending on whether 'y' represents one or
+    // the other.
+    // Values will be up to 255 * 16 * 16 = 65280.
+    // (y * (Aa2 * (16 - x1) + Aa3 * x1), ... ,
+    //  y * (Ra0 * (16 - x0) + Ra1 * x0))
+    sum = _mm_mullo_epi16(sum, y);
+
+    return sum;
+}
+
+// Process two pixel pairs out of eight input pixels.
+// In other methods, the distinct pixels are passed one by one, but in this
+// case, the rows, and index offsets to the pixels into the row are passed
+// to generate the 8 pixels.
+// @param row0..1 top and bottom row where to find input pixels.
+// @param x0..1 offsets into the row for all eight input pixels.
+// @param all_y vector of 16 bit components containing the constant sub_y
+// @param neg_y vector of 16 bit components containing the constant 16 - sub_y
+// @param alpha vector of 16 bit components containing the alpha value to scale
+//        the results by, when has_alpha is true.
+// @return
+// (alpha * ((16-y) * (Aa2  * (16-x1) + Aa3  * x1) +
+//             y    * (Aa2' * (16-x1) + Aa3' * x1)),
+// ...
+//  alpha * ((16-y) * (Ra0  * (16-x0) + Ra1 * x0) +
+//             y    * (Ra0' * (16-x0) + Ra1' * x0))
+// With the factor alpha removed when has_alpha is false.
+// The values are scaled back to 16 bit components, but with only the bottom
+// 8 bits being set.
+template<bool has_alpha>
+inline __m128i ProcessTwoPixelPairs(const uint32_t* row0,
+                                    const uint32_t* row1,
+                                    const int* x0,
+                                    const int* x1,
+                                    const __m128i& scale_x,
+                                    const __m128i& all_y,
+                                    const __m128i& neg_y,
+                                    const __m128i& alpha) {
+    __m128i sum0 = ProcessPixelPair(
+        row0[x0[0]], row0[x1[0]], row0[x0[1]], row0[x1[1]],
+        scale_x, neg_y);
+    __m128i sum1 = ProcessPixelPair(
+        row1[x0[0]], row1[x1[0]], row1[x0[1]], row1[x1[1]],
+        scale_x, all_y);
+
+    // 2 samples fully summed.
+    // ((16-y) * (Aa2 * (16-x1) + Aa3 * x1) +
+    //  y * (Aa2' * (16-x1) + Aa3' * x1),
+    // ...
+    //  (16-y) * (Ra0 * (16 - x0) + Ra1 * x0)) +
+    //  y * (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.
+inline __m128i ProcessOnePixel(uint32_t pixel0, uint32_t pixel1,
+                               const __m128i& scale_x, const __m128i& y) {
+    __m128i a0 = _mm_cvtsi32_si128(pixel0);
+    __m128i a1 = _mm_cvtsi32_si128(pixel1);
+
+    // Interleave
+    // (0, 0, 0, 0, 0, 0, 0, 0, Aa1, Aa0, Ba1, Ba0, Ga1, Ga0, Ra1, Ra0)
+    a0 = _mm_unpacklo_epi8(a0, a1);
+
+    // (a0 * (16-x) + a1 * x)
+    a0 = _mm_maddubs_epi16(a0, scale_x);
+
+    // scale row by y
+    return _mm_mullo_epi16(a0, y);
+}
+
+// Notes about the various tricks that are used in this implementation:
+// - specialization for sub_y == 0.
+// Statistically, 1/16th of the samples will have sub_y == 0. When this
+// happens, the math goes from:
+// (16 - x)*(16 - y)*a00 + x*(16 - y)*a01 + (16 - x)*y*a10 + x*y*a11
+// to:
+// (16 - x)*a00 + 16*x*a01
+// much simpler. The simplification makes for an easy boost in performance.
+// - calculating 4 output pixels at a time.
+//  This allows loading the coefficients x0 and x1 and shuffling them to the
+// optimum location only once per loop, instead of twice per loop.
+// This also allows us to store the four pixels with a single store.
+// - Use of 2 special SSSE3 instructions (comparatively to the SSE2 instruction
+// version):
+// _mm_shuffle_epi8 : this allows us to spread the coefficients x[0-3] loaded
+// in 32 bit values to 8 bit values repeated four times.
+// _mm_maddubs_epi16 : this allows us to perform multiplications and additions
+// in one swoop of 8bit values storing the results in 16 bit values. This
+// instruction is actually crucial for the speed of the implementation since
+// as one can see in the SSE2 implementation, all inputs have to be used as
+// 16 bits because the results are 16 bits. This basically allows us to process
+// twice as many pixel components per iteration.
+//
+// As a result, this method behaves faster than the traditional SSE2. The actual
+// boost varies greatly on the underlying architecture.
+template<bool has_alpha>
+void S32_generic_D32_filter_DX_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();
+    const uint32_t XY = *xy++;
+    const unsigned y0 = XY >> 14;
+    const uint32_t* row0 =
+            reinterpret_cast<const uint32_t*>(src_addr + (y0 >> 4) * rb);
+    const uint32_t* row1 =
+            reinterpret_cast<const uint32_t*>(src_addr + (XY & 0x3FFF) * rb);
+    const unsigned sub_y = y0 & 0xF;
+
+    // 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);
+    // (0, 0, 0, 0, 0, 0, 0, 0)
+    const __m128i zero = _mm_setzero_si128();
+
+    __m128i alpha;
+    if (has_alpha)
+        // 8x(alpha)
+        alpha = _mm_set1_epi16(s.fAlphaScale);
+
+    if (sub_y == 0) {
+        // Unroll 4x, interleave bytes, use pmaddubsw (all_x is small)
+        while (count > 3) {
+            count -= 4;
+
+            int x0[4];
+            int x1[4];
+            __m128i all_x, sixteen_minus_x;
+            PrepareConstantsTwoPixelPairs(xy, mask_3FFF, mask_000F,
+                                          sixteen_8bit, mask_dist_select,
+                                          &all_x, &sixteen_minus_x, x0, x1);
+            xy += 4;
+
+            // First pair of pixel pairs.
+            // (4x(x1, 16-x1), 4x(x0, 16-x0))
+            __m128i scale_x;
+            scale_x = _mm_unpacklo_epi8(sixteen_minus_x, all_x);
+
+            __m128i sum0 = ProcessPixelPairZeroSubY<has_alpha>(
+                row0[x0[0]], row0[x1[0]], row0[x0[1]], row0[x1[1]],
+                scale_x, alpha);
+
+            // second pair of pixel pairs
+            // (4x (x3, 16-x3), 4x (16-x2, x2))
+            scale_x = _mm_unpackhi_epi8(sixteen_minus_x, all_x);
+
+            __m128i sum1 = ProcessPixelPairZeroSubY<has_alpha>(
+                row0[x0[2]], row0[x1[2]], row0[x0[3]], row0[x1[3]],
+                scale_x, alpha);
+
+            // Pack lower 4 16 bit values of sum into lower 4 bytes.
+            sum0 = _mm_packus_epi16(sum0, sum1);
+
+            // Extract low int and store.
+            _mm_storeu_si128(reinterpret_cast<__m128i *>(colors), sum0);
+
+            colors += 4;
+        }
+
+        // handle remainder
+        while (count-- > 0) {
+            uint32_t xx = *xy++;  // x0:14 | 4 | x1:14
+            unsigned x0 = xx >> 18;
+            unsigned x1 = xx & 0x3FFF;
+
+            // 16x(x)
+            const __m128i all_x = _mm_set1_epi8((xx >> 14) & 0x0F);
+
+            // (16x(16-x))
+            __m128i scale_x = _mm_sub_epi8(sixteen_8bit, all_x);
+
+            scale_x = _mm_unpacklo_epi8(scale_x, all_x);
+
+            __m128i sum = ProcessOnePixelZeroSubY<has_alpha>(
+                row0[x0], row0[x1],
+                scale_x, alpha);
+
+            // Pack lower 4 16 bit values of sum into lower 4 bytes.
+            sum = _mm_packus_epi16(sum, zero);
+
+            // Extract low int and store.
+            *colors++ = _mm_cvtsi128_si32(sum);
+        }
+    } else {  // more general case, y != 0
+        // 8x(16)
+        const __m128i sixteen_16bit = _mm_set1_epi16(16);
+
+        // 8x (y)
+        const __m128i all_y = _mm_set1_epi16(sub_y);
+
+        // 8x (16-y)
+        const __m128i neg_y = _mm_sub_epi16(sixteen_16bit, all_y);
+
+        // Unroll 4x, interleave bytes, use pmaddubsw (all_x is small)
+        while (count > 3) {
+            count -= 4;
+
+            int x0[4];
+            int x1[4];
+            __m128i all_x, sixteen_minus_x;
+            PrepareConstantsTwoPixelPairs(xy, mask_3FFF, mask_000F,
+                                          sixteen_8bit, mask_dist_select,
+                                          &all_x, &sixteen_minus_x, x0, x1);
+            xy += 4;
+
+            // First pair of pixel pairs
+            // (4x(x1, 16-x1), 4x(x0, 16-x0))
+            __m128i scale_x;
+            scale_x = _mm_unpacklo_epi8(sixteen_minus_x, all_x);
+
+            __m128i sum0 = ProcessTwoPixelPairs<has_alpha>(
+                row0, row1, x0, x1,
+                scale_x, all_y, neg_y, alpha);
+
+            // second pair of pixel pairs
+            // (4x (x3, 16-x3), 4x (16-x2, x2))
+            scale_x = _mm_unpackhi_epi8(sixteen_minus_x, all_x);
+
+            __m128i sum1 = ProcessTwoPixelPairs<has_alpha>(
+                row0, row1, x0 + 2, x1 + 2,
+                scale_x, all_y, neg_y, alpha);
+
+            // Do the final packing of the two results
+
+            // Pack lower 4 16 bit values of sum into lower 4 bytes.
+            sum0 = _mm_packus_epi16(sum0, sum1);
+
+            // Extract low int and store.
+            _mm_storeu_si128(reinterpret_cast<__m128i *>(colors), sum0);
+
+            colors += 4;
+        }
+
+        // Left over.
+        while (count-- > 0) {
+            const uint32_t xx = *xy++;  // x0:14 | 4 | x1:14
+            const unsigned x0 = xx >> 18;
+            const unsigned x1 = xx & 0x3FFF;
+
+            // 16x(x)
+            const __m128i all_x = _mm_set1_epi8((xx >> 14) & 0x0F);
+
+            // 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);
+
+            // 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, zero);
+
+            // Extract low int and store.
+            *colors++ = _mm_cvtsi128_si32(sum0);
+        }
+    }
+}
+}  // namepace
+
+void S32_opaque_D32_filter_DX_SSSE3(const SkBitmapProcState& s,
+                                    const uint32_t* xy,
+                                    int count, uint32_t* colors) {
+    S32_generic_D32_filter_DX_SSSE3<false>(s, xy, count, colors);
+}
+
+void S32_alpha_D32_filter_DX_SSSE3(const SkBitmapProcState& s,
+                                   const uint32_t* xy,
+                                   int count, uint32_t* colors) {
+    S32_generic_D32_filter_DX_SSSE3<true>(s, xy, count, colors);
+}
diff --git a/legacy/src/opts/SkBitmapProcState_opts_SSSE3.h b/legacy/src/opts/SkBitmapProcState_opts_SSSE3.h
new file mode 100644
index 0000000..d21e7e4
--- /dev/null
+++ b/legacy/src/opts/SkBitmapProcState_opts_SSSE3.h
@@ -0,0 +1,15 @@
+/*
+ * 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 "SkBitmapProcState.h"
+
+void S32_opaque_D32_filter_DX_SSSE3(const SkBitmapProcState& s,
+                                    const uint32_t* xy,
+                                    int count, uint32_t* colors);
+void S32_alpha_D32_filter_DX_SSSE3(const SkBitmapProcState& s,
+                                   const uint32_t* xy,
+                                   int count, uint32_t* colors);
diff --git a/legacy/src/opts/SkBitmapProcState_opts_arm.cpp b/legacy/src/opts/SkBitmapProcState_opts_arm.cpp
new file mode 100644
index 0000000..20d62e1
--- /dev/null
+++ b/legacy/src/opts/SkBitmapProcState_opts_arm.cpp
@@ -0,0 +1,221 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkBitmapProcState.h"
+#include "SkColorPriv.h"
+#include "SkUtils.h"
+
+#if __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")));
+
+void SI8_D16_nofilter_DX_arm(const SkBitmapProcState& s,
+                             const uint32_t* SK_RESTRICT xy,
+                             int count, uint16_t* SK_RESTRICT colors) {
+    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];
+        sk_memset16(colors, dstValue, count);
+    } else {
+        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
+                      "1:                                             \n\t"
+                      "ldmia      %[xx]!, {r5, r7, r9, r11}       \n\t"   // load ptrs to pixels 0-7
+                      "subs       %[count8], %[count8], #1        \n\t"   // decrement loop counter
+                      "uxth       r4, r5                          \n\t"   // extract ptr 0
+                      "mov        r5, r5, lsr #16                 \n\t"   // extract ptr 1
+                      "uxth       r6, r7                          \n\t"   // extract ptr 2
+                      "mov        r7, r7, lsr #16                 \n\t"   // extract ptr 3
+                      "ldrb       r4, [%[srcAddr], r4]            \n\t"   // load pixel 0 from image
+                      "uxth       r8, r9                          \n\t"   // extract ptr 4
+                      "ldrb       r5, [%[srcAddr], r5]            \n\t"   // load pixel 1 from image
+                      "mov        r9, r9, lsr #16                 \n\t"   // extract ptr 5
+                      "ldrb       r6, [%[srcAddr], r6]            \n\t"   // load pixel 2 from image
+                      "uxth       r10, r11                        \n\t"   // extract ptr 6
+                      "ldrb       r7, [%[srcAddr], r7]            \n\t"   // load pixel 3 from image
+                      "mov        r11, r11, lsr #16               \n\t"   // extract ptr 7
+                      "ldrb       r8, [%[srcAddr], r8]            \n\t"   // load pixel 4 from image
+                      "add        r4, r4, r4                      \n\t"   // double pixel 0 for RGB565 lookup
+                      "ldrb       r9, [%[srcAddr], r9]            \n\t"   // load pixel 5 from image
+                      "add        r5, r5, r5                      \n\t"   // double pixel 1 for RGB565 lookup
+                      "ldrb       r10, [%[srcAddr], r10]          \n\t"   // load pixel 6 from image
+                      "add        r6, r6, r6                      \n\t"   // double pixel 2 for RGB565 lookup
+                      "ldrb       r11, [%[srcAddr], r11]          \n\t"   // load pixel 7 from image
+                      "add        r7, r7, r7                      \n\t"   // double pixel 3 for RGB565 lookup
+                      "ldrh       r4, [%[table], r4]              \n\t"   // load pixel 0 RGB565 from colmap
+                      "add        r8, r8, r8                      \n\t"   // double pixel 4 for RGB565 lookup
+                      "ldrh       r5, [%[table], r5]              \n\t"   // load pixel 1 RGB565 from colmap
+                      "add        r9, r9, r9                      \n\t"   // double pixel 5 for RGB565 lookup
+                      "ldrh       r6, [%[table], r6]              \n\t"   // load pixel 2 RGB565 from colmap
+                      "add        r10, r10, r10                   \n\t"   // double pixel 6 for RGB565 lookup
+                      "ldrh       r7, [%[table], r7]              \n\t"   // load pixel 3 RGB565 from colmap
+                      "add        r11, r11, r11                   \n\t"   // double pixel 7 for RGB565 lookup
+                      "ldrh       r8, [%[table], r8]              \n\t"   // load pixel 4 RGB565 from colmap
+                      "ldrh       r9, [%[table], r9]              \n\t"   // load pixel 5 RGB565 from colmap
+                      "ldrh       r10, [%[table], r10]            \n\t"   // load pixel 6 RGB565 from colmap
+                      "ldrh       r11, [%[table], r11]            \n\t"   // load pixel 7 RGB565 from colmap
+                      "pkhbt      r5, r4, r5, lsl #16             \n\t"   // pack pixels 0 and 1
+                      "pkhbt      r6, r6, r7, lsl #16             \n\t"   // pack pixels 2 and 3
+                      "pkhbt      r8, r8, r9, lsl #16             \n\t"   // pack pixels 4 and 5
+                      "pkhbt      r10, r10, r11, lsl #16          \n\t"   // pack pixels 6 and 7
+                      "stmia      %[colors]!, {r5, r6, r8, r10}   \n\t"   // store last 8 pixels
+                      "bgt        1b                              \n\t"   // loop if counter > 0
+                      "2:                                             \n\t"
+                      : [xx] "+r" (xx), [count8] "+r" (count8), [colors] "+r" (colors)
+                      : [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(); 
+}
+
+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")));
+
+void SI8_opaque_D32_nofilter_DX_arm(const SkBitmapProcState& s,
+                                    const uint32_t* SK_RESTRICT xy,
+                                    int count, SkPMColor* SK_RESTRICT colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
+    SkASSERT(s.fDoFilter == false);
+
+    const SkPMColor* SK_RESTRICT table = s.fBitmap->getColorTable()->lockColors();
+    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());
+
+    if (1 == s.fBitmap->width()) {
+        uint8_t src = srcAddr[0];
+        SkPMColor dstValue = table[src];
+        sk_memset32(colors, dstValue, count);
+    } else {
+        const uint16_t* xx = (const uint16_t*)(xy + 1);
+
+        asm volatile (
+                      "subs       %[count], %[count], #8          \n\t"   // decrement count by 8, set flags
+                      "blt        2f                              \n\t"   // if count < 0, branch to singles
+                      "1:                                             \n\t"   // eights loop
+                      "ldmia      %[xx]!, {r5, r7, r9, r11}       \n\t"   // load ptrs to pixels 0-7
+                      "uxth       r4, r5                          \n\t"   // extract ptr 0
+                      "mov        r5, r5, lsr #16                 \n\t"   // extract ptr 1
+                      "uxth       r6, r7                          \n\t"   // extract ptr 2
+                      "mov        r7, r7, lsr #16                 \n\t"   // extract ptr 3
+                      "ldrb       r4, [%[srcAddr], r4]            \n\t"   // load pixel 0 from image
+                      "uxth       r8, r9                          \n\t"   // extract ptr 4
+                      "ldrb       r5, [%[srcAddr], r5]            \n\t"   // load pixel 1 from image
+                      "mov        r9, r9, lsr #16                 \n\t"   // extract ptr 5
+                      "ldrb       r6, [%[srcAddr], r6]            \n\t"   // load pixel 2 from image
+                      "uxth       r10, r11                        \n\t"   // extract ptr 6
+                      "ldrb       r7, [%[srcAddr], r7]            \n\t"   // load pixel 3 from image
+                      "mov        r11, r11, lsr #16               \n\t"   // extract ptr 7
+                      "ldrb       r8, [%[srcAddr], r8]            \n\t"   // load pixel 4 from image
+                      "ldrb       r9, [%[srcAddr], r9]            \n\t"   // load pixel 5 from image
+                      "ldrb       r10, [%[srcAddr], r10]          \n\t"   // load pixel 6 from image
+                      "ldrb       r11, [%[srcAddr], r11]          \n\t"   // load pixel 7 from image
+                      "ldr        r4, [%[table], r4, lsl #2]      \n\t"   // load pixel 0 SkPMColor from colmap
+                      "ldr        r5, [%[table], r5, lsl #2]      \n\t"   // load pixel 1 SkPMColor from colmap
+                      "ldr        r6, [%[table], r6, lsl #2]      \n\t"   // load pixel 2 SkPMColor from colmap
+                      "ldr        r7, [%[table], r7, lsl #2]      \n\t"   // load pixel 3 SkPMColor from colmap
+                      "ldr        r8, [%[table], r8, lsl #2]      \n\t"   // load pixel 4 SkPMColor from colmap
+                      "ldr        r9, [%[table], r9, lsl #2]      \n\t"   // load pixel 5 SkPMColor from colmap
+                      "ldr        r10, [%[table], r10, lsl #2]    \n\t"   // load pixel 6 SkPMColor from colmap
+                      "ldr        r11, [%[table], r11, lsl #2]    \n\t"   // load pixel 7 SkPMColor from colmap
+                      "subs       %[count], %[count], #8          \n\t"   // decrement loop counter
+                      "stmia      %[colors]!, {r4-r11}            \n\t"   // store 8 pixels
+                      "bge        1b                              \n\t"   // loop if counter >= 0
+                      "2:                                             \n\t"
+                      "adds       %[count], %[count], #8          \n\t"   // fix up counter, set flags
+                      "beq        4f                              \n\t"   // if count == 0, branch to exit
+                      "3:                                             \n\t"   // singles loop
+                      "ldrh       r4, [%[xx]], #2                 \n\t"   // load pixel ptr
+                      "subs       %[count], %[count], #1          \n\t"   // decrement loop counter
+                      "ldrb       r5, [%[srcAddr], r4]            \n\t"   // load pixel from image
+                      "ldr        r6, [%[table], r5, lsl #2]      \n\t"   // load SkPMColor from colmap
+                      "str        r6, [%[colors]], #4             \n\t"   // store pixel, update ptr
+                      "bne        3b                              \n\t"   // loop if counter != 0
+                      "4:                                             \n\t"   // exit
+                      : [xx] "+r" (xx), [count] "+r" (count), [colors] "+r" (colors)
+                      : [table] "r" (table), [srcAddr] "r" (srcAddr)
+                      : "memory", "cc", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11"
+                      );
+    }
+
+    s.fBitmap->getColorTable()->unlockColors(false);
+}
+#endif //__ARM_ARCH__ >= 6 && !defined(SK_CPU_BENDIAN)
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  If we replace a sampleproc, then we null-out the associated shaderproc,
+    otherwise the shader won't even look at the matrix/sampler
+ */
+void SkBitmapProcState::platformProcs() {
+    bool doFilter = fDoFilter;
+    bool isOpaque = 256 == fAlphaScale;
+    bool justDx = false;
+
+    if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
+        justDx = true;
+    }
+
+    switch (fBitmap->config()) {
+        case SkBitmap::kIndex8_Config:
+#if __ARM_ARCH__ >= 6 && !defined(SK_CPU_BENDIAN)
+            if (justDx && !doFilter) {
+#if 0   /* crashing on android device */
+                fSampleProc16 = SI8_D16_nofilter_DX_arm;
+                fShaderProc16 = NULL;
+#endif
+                if (isOpaque) {
+                    // this one is only very slighty faster than the C version
+                    fSampleProc32 = SI8_opaque_D32_nofilter_DX_arm;
+                    fShaderProc32 = NULL;
+                }
+            }
+#endif
+            break;
+        default:
+            break;
+    }
+}
+
diff --git a/legacy/src/opts/SkBitmapProcState_opts_none.cpp b/legacy/src/opts/SkBitmapProcState_opts_none.cpp
new file mode 100644
index 0000000..82be4ea
--- /dev/null
+++ b/legacy/src/opts/SkBitmapProcState_opts_none.cpp
@@ -0,0 +1,25 @@
+
+/*
+ * 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 "SkBitmapProcState.h"
+
+/*  A platform may optionally overwrite any of these with accelerated
+    versions. On input, these will already have valid function pointers,
+    so a platform need only overwrite the ones it chooses, based on the
+    current state (e.g. fBitmap, fInvMatrix, etc.)
+
+    fShaderProc32
+    fShaderProc16
+    fMatrixProc
+    fSampleProc32
+    fSampleProc32
+ */
+
+// empty implementation just uses default supplied function pointers
+void SkBitmapProcState::platformProcs() {}
+
+
diff --git a/legacy/src/opts/SkBlitRow_opts_SSE2.cpp b/legacy/src/opts/SkBlitRow_opts_SSE2.cpp
new file mode 100644
index 0000000..8e4dd1d
--- /dev/null
+++ b/legacy/src/opts/SkBlitRow_opts_SSE2.cpp
@@ -0,0 +1,736 @@
+/*
+ * 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_SSE2.h"
+#include "SkColorPriv.h"
+#include "SkUtils.h"
+
+#include <emmintrin.h>
+
+/* SSE2 version of S32_Blend_BlitRow32()
+ * portable version is in core/SkBlitRow_D32.cpp
+ */
+void S32_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src,
+                              int count, U8CPU alpha) {
+    SkASSERT(alpha <= 255);
+    if (count <= 0) {
+        return;
+    }
+
+    uint32_t src_scale = SkAlpha255To256(alpha);
+    uint32_t dst_scale = 256 - src_scale;
+
+    if (count >= 4) {
+        SkASSERT(((size_t)dst & 0x03) == 0);
+        while (((size_t)dst & 0x0F) != 0) {
+            *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
+            src++;
+            dst++;
+            count--;
+        }
+
+        const __m128i *s = reinterpret_cast<const __m128i*>(src);
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+        __m128i rb_mask = _mm_set1_epi32(0x00FF00FF);
+        __m128i ag_mask = _mm_set1_epi32(0xFF00FF00);
+
+        // Move scale factors to upper byte of word
+        __m128i src_scale_wide = _mm_set1_epi16(src_scale << 8);
+        __m128i dst_scale_wide = _mm_set1_epi16(dst_scale << 8);
+        while (count >= 4) {
+            // Load 4 pixels each of src and dest.
+            __m128i src_pixel = _mm_loadu_si128(s);
+            __m128i dst_pixel = _mm_load_si128(d);
+
+            // Interleave Atom port 0/1 operations based on the execution port
+            // constraints that multiply can only be executed on port 0 (while
+            // boolean operations can be executed on either port 0 or port 1)
+            // because GCC currently doesn't do a good job scheduling
+            // instructions based on these constraints.
+
+            // Get red and blue pixels into lower byte of each word.
+            // (0, r, 0, b, 0, r, 0, b, 0, r, 0, b, 0, r, 0, b)
+            __m128i src_rb = _mm_and_si128(rb_mask, src_pixel);
+
+            // Multiply by scale.
+            // (4 x (0, rs.h, 0, bs.h))
+            // where rs.h stands for the higher byte of r * scale, and
+            // bs.h the higher byte of b * scale.
+            src_rb = _mm_mulhi_epu16(src_rb, src_scale_wide);
+
+            // Get alpha and green pixels into higher byte of each word.
+            // (a, 0, g, 0, a, 0, g, 0, a, 0, g, 0, a, 0, g, 0)
+            __m128i src_ag = _mm_and_si128(ag_mask, src_pixel);
+
+            // Multiply by scale.
+            // (4 x (as.h, as.l, gs.h, gs.l))
+            src_ag = _mm_mulhi_epu16(src_ag, src_scale_wide);
+
+            // Clear the lower byte of the a*scale and g*scale results
+            // (4 x (as.h, 0, gs.h, 0))
+            src_ag = _mm_and_si128(src_ag, ag_mask);
+
+            // Operations the destination pixels are the same as on the
+            // source pixels. See the comments above.
+            __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel);
+            dst_rb = _mm_mulhi_epu16(dst_rb, dst_scale_wide);
+            __m128i dst_ag = _mm_and_si128(ag_mask, dst_pixel);
+            dst_ag = _mm_mulhi_epu16(dst_ag, dst_scale_wide);
+            dst_ag = _mm_and_si128(dst_ag, ag_mask);
+
+            // Combine back into RGBA.
+            // (4 x (as.h, rs.h, gs.h, bs.h))
+            src_pixel = _mm_or_si128(src_rb, src_ag);
+            dst_pixel = _mm_or_si128(dst_rb, dst_ag);
+
+            // Add result
+            __m128i result = _mm_add_epi8(src_pixel, dst_pixel);
+            _mm_store_si128(d, result);
+            s++;
+            d++;
+            count -= 4;
+        }
+        src = reinterpret_cast<const SkPMColor*>(s);
+        dst = reinterpret_cast<SkPMColor*>(d);
+    }
+
+    while (count > 0) {
+        *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
+        src++;
+        dst++;
+        count--;
+    }
+}
+
+void S32A_Opaque_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
+                                const SkPMColor* SK_RESTRICT src,
+                                int count, U8CPU alpha) {
+    SkASSERT(alpha == 255);
+    if (count <= 0) {
+        return;
+    }
+
+    if (count >= 4) {
+        SkASSERT(((size_t)dst & 0x03) == 0);
+        while (((size_t)dst & 0x0F) != 0) {
+            *dst = SkPMSrcOver(*src, *dst);
+            src++;
+            dst++;
+            count--;
+        }
+
+        const __m128i *s = reinterpret_cast<const __m128i*>(src);
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+#ifdef SK_USE_ACCURATE_BLENDING
+        __m128i rb_mask = _mm_set1_epi32(0x00FF00FF);
+        __m128i c_128 = _mm_set1_epi16(128);  // 8 copies of 128 (16-bit)
+        __m128i c_255 = _mm_set1_epi16(255);  // 8 copies of 255 (16-bit)
+        while (count >= 4) {
+            // Load 4 pixels
+            __m128i src_pixel = _mm_loadu_si128(s);
+            __m128i dst_pixel = _mm_load_si128(d);
+
+            __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel);
+            __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8);
+            // Shift alphas down to lower 8 bits of each quad.
+            __m128i alpha = _mm_srli_epi32(src_pixel, 24);
+
+            // Copy alpha to upper 3rd byte of each quad
+            alpha = _mm_or_si128(alpha, _mm_slli_epi32(alpha, 16));
+
+            // Subtract alphas from 255, to get 0..255
+            alpha = _mm_sub_epi16(c_255, alpha);
+
+            // Multiply by red and blue by src alpha.
+            dst_rb = _mm_mullo_epi16(dst_rb, alpha);
+            // Multiply by alpha and green by src alpha.
+            dst_ag = _mm_mullo_epi16(dst_ag, alpha);
+
+            // dst_rb_low = (dst_rb >> 8)
+            __m128i dst_rb_low = _mm_srli_epi16(dst_rb, 8);
+            __m128i dst_ag_low = _mm_srli_epi16(dst_ag, 8);
+
+            // dst_rb = (dst_rb + dst_rb_low + 128) >> 8
+            dst_rb = _mm_add_epi16(dst_rb, dst_rb_low);
+            dst_rb = _mm_add_epi16(dst_rb, c_128);
+            dst_rb = _mm_srli_epi16(dst_rb, 8);
+
+            // dst_ag = (dst_ag + dst_ag_low + 128) & ag_mask
+            dst_ag = _mm_add_epi16(dst_ag, dst_ag_low);
+            dst_ag = _mm_add_epi16(dst_ag, c_128);
+            dst_ag = _mm_andnot_si128(rb_mask, dst_ag);
+
+            // Combine back into RGBA.
+            dst_pixel = _mm_or_si128(dst_rb, dst_ag);
+
+            // Add result
+            __m128i result = _mm_add_epi8(src_pixel, dst_pixel);
+            _mm_store_si128(d, result);
+            s++;
+            d++;
+            count -= 4;
+        }
+    #else
+        __m128i rb_mask = _mm_set1_epi32(0x00FF00FF);
+        __m128i c_256 = _mm_set1_epi16(0x0100);  // 8 copies of 256 (16-bit)
+        while (count >= 4) {
+            // Load 4 pixels
+            __m128i src_pixel = _mm_loadu_si128(s);
+            __m128i dst_pixel = _mm_load_si128(d);
+
+            __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel);
+            __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8);
+
+            // (a0, g0, a1, g1, a2, g2, a3, g3)  (low byte of each word)
+            __m128i alpha = _mm_srli_epi16(src_pixel, 8);
+
+            // (a0, a0, a1, a1, a2, g2, a3, g3)
+            alpha = _mm_shufflehi_epi16(alpha, 0xF5);
+
+            // (a0, a0, a1, a1, a2, a2, a3, a3)
+            alpha = _mm_shufflelo_epi16(alpha, 0xF5);
+
+            // Subtract alphas from 256, to get 1..256
+            alpha = _mm_sub_epi16(c_256, alpha);
+
+            // Multiply by red and blue by src alpha.
+            dst_rb = _mm_mullo_epi16(dst_rb, alpha);
+            // Multiply by alpha and green by src alpha.
+            dst_ag = _mm_mullo_epi16(dst_ag, alpha);
+
+            // Divide by 256.
+            dst_rb = _mm_srli_epi16(dst_rb, 8);
+
+            // Mask out high bits (already in the right place)
+            dst_ag = _mm_andnot_si128(rb_mask, dst_ag);
+
+            // Combine back into RGBA.
+            dst_pixel = _mm_or_si128(dst_rb, dst_ag);
+
+            // Add result
+            __m128i result = _mm_add_epi8(src_pixel, dst_pixel);
+            _mm_store_si128(d, result);
+            s++;
+            d++;
+            count -= 4;
+        }
+#endif
+        src = reinterpret_cast<const SkPMColor*>(s);
+        dst = reinterpret_cast<SkPMColor*>(d);
+    }
+
+    while (count > 0) {
+        *dst = SkPMSrcOver(*src, *dst);
+        src++;
+        dst++;
+        count--;
+    }
+}
+
+void S32A_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
+                               const SkPMColor* SK_RESTRICT src,
+                               int count, U8CPU alpha) {
+    SkASSERT(alpha <= 255);
+    if (count <= 0) {
+        return;
+    }
+
+    if (count >= 4) {
+        while (((size_t)dst & 0x0F) != 0) {
+            *dst = SkBlendARGB32(*src, *dst, alpha);
+            src++;
+            dst++;
+            count--;
+        }
+
+        uint32_t src_scale = SkAlpha255To256(alpha);
+
+        const __m128i *s = reinterpret_cast<const __m128i*>(src);
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+        __m128i src_scale_wide = _mm_set1_epi16(src_scale << 8);
+        __m128i rb_mask = _mm_set1_epi32(0x00FF00FF);
+        __m128i c_256 = _mm_set1_epi16(256);  // 8 copies of 256 (16-bit)
+        while (count >= 4) {
+            // Load 4 pixels each of src and dest.
+            __m128i src_pixel = _mm_loadu_si128(s);
+            __m128i dst_pixel = _mm_load_si128(d);
+
+            // Get red and blue pixels into lower byte of each word.
+            __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel);
+            __m128i src_rb = _mm_and_si128(rb_mask, src_pixel);
+
+            // Get alpha and green into lower byte of each word.
+            __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8);
+            __m128i src_ag = _mm_srli_epi16(src_pixel, 8);
+
+            // Put per-pixel alpha in low byte of each word.
+            // After the following two statements, the dst_alpha looks like
+            // (0, a0, 0, a0, 0, a1, 0, a1, 0, a2, 0, a2, 0, a3, 0, a3)
+            __m128i dst_alpha = _mm_shufflehi_epi16(src_ag, 0xF5);
+            dst_alpha = _mm_shufflelo_epi16(dst_alpha, 0xF5);
+
+            // dst_alpha = dst_alpha * src_scale
+            // Because src_scales are in the higher byte of each word and
+            // we use mulhi here, the resulting alpha values are already
+            // in the right place and don't need to be divided by 256.
+            // (0, sa0, 0, sa0, 0, sa1, 0, sa1, 0, sa2, 0, sa2, 0, sa3, 0, sa3)
+            dst_alpha = _mm_mulhi_epu16(dst_alpha, src_scale_wide);
+
+            // Subtract alphas from 256, to get 1..256
+            dst_alpha = _mm_sub_epi16(c_256, dst_alpha);
+
+            // Multiply red and blue by dst pixel alpha.
+            dst_rb = _mm_mullo_epi16(dst_rb, dst_alpha);
+            // Multiply alpha and green by dst pixel alpha.
+            dst_ag = _mm_mullo_epi16(dst_ag, dst_alpha);
+
+            // Multiply red and blue by global alpha.
+            // (4 x (0, rs.h, 0, bs.h))
+            // where rs.h stands for the higher byte of r * src_scale,
+            // and bs.h the higher byte of b * src_scale.
+            // Again, because we use mulhi, the resuling red and blue
+            // values are already in the right place and don't need to
+            // be divided by 256.
+            src_rb = _mm_mulhi_epu16(src_rb, src_scale_wide);
+            // Multiply alpha and green by global alpha.
+            // (4 x (0, as.h, 0, gs.h))
+            src_ag = _mm_mulhi_epu16(src_ag, src_scale_wide);
+
+            // Divide by 256.
+            dst_rb = _mm_srli_epi16(dst_rb, 8);
+
+            // Mask out low bits (goodies already in the right place; no need to divide)
+            dst_ag = _mm_andnot_si128(rb_mask, dst_ag);
+            // Shift alpha and green to higher byte of each word.
+            // (4 x (as.h, 0, gs.h, 0))
+            src_ag = _mm_slli_epi16(src_ag, 8);
+
+            // Combine back into RGBA.
+            dst_pixel = _mm_or_si128(dst_rb, dst_ag);
+            src_pixel = _mm_or_si128(src_rb, src_ag);
+
+            // Add two pixels into result.
+            __m128i result = _mm_add_epi8(src_pixel, dst_pixel);
+            _mm_store_si128(d, result);
+            s++;
+            d++;
+            count -= 4;
+        }
+        src = reinterpret_cast<const SkPMColor*>(s);
+        dst = reinterpret_cast<SkPMColor*>(d);
+    }
+
+    while (count > 0) {
+        *dst = SkBlendARGB32(*src, *dst, alpha);
+        src++;
+        dst++;
+        count--;
+    }
+}
+
+/* SSE2 version of Color32()
+ * portable version is in core/SkBlitRow_D32.cpp
+ */
+void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count,
+                  SkPMColor color) {
+
+    if (count <= 0) {
+        return;
+    }
+
+    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 >= 4) {
+            SkASSERT(((size_t)dst & 0x03) == 0);
+            while (((size_t)dst & 0x0F) != 0) {
+                *dst = color + SkAlphaMulQ(*src, scale);
+                src++;
+                dst++;
+                count--;
+            }
+
+            const __m128i *s = reinterpret_cast<const __m128i*>(src);
+            __m128i *d = reinterpret_cast<__m128i*>(dst);
+            __m128i rb_mask = _mm_set1_epi32(0x00FF00FF);
+            __m128i src_scale_wide = _mm_set1_epi16(scale);
+            __m128i color_wide = _mm_set1_epi32(color);
+            while (count >= 4) {
+                // Load 4 pixels each of src and dest.
+                __m128i src_pixel = _mm_loadu_si128(s);
+
+                // Get red and blue pixels into lower byte of each word.
+                __m128i src_rb = _mm_and_si128(rb_mask, src_pixel);
+
+                // Get alpha and green into lower byte of each word.
+                __m128i src_ag = _mm_srli_epi16(src_pixel, 8);
+
+                // Multiply by scale.
+                src_rb = _mm_mullo_epi16(src_rb, src_scale_wide);
+                src_ag = _mm_mullo_epi16(src_ag, src_scale_wide);
+
+                // Divide by 256.
+                src_rb = _mm_srli_epi16(src_rb, 8);
+                src_ag = _mm_andnot_si128(rb_mask, src_ag);
+
+                // Combine back into RGBA.
+                src_pixel = _mm_or_si128(src_rb, src_ag);
+
+                // Add color to result.
+                __m128i result = _mm_add_epi8(color_wide, src_pixel);
+
+                // Store result.
+                _mm_store_si128(d, result);
+                s++;
+                d++;
+                count -= 4;
+            }
+            src = reinterpret_cast<const SkPMColor*>(s);
+            dst = reinterpret_cast<SkPMColor*>(d);
+         }
+
+        while (count > 0) {
+            *dst = color + SkAlphaMulQ(*src, scale);
+            src += 1;
+            dst += 1;
+            count--;
+        }
+    }
+}
+
+void SkARGB32_A8_BlitMask_SSE2(void* device, size_t dstRB, const void* maskPtr,
+                               size_t maskRB, SkColor origColor,
+                               int width, int height) {
+    SkPMColor color = SkPreMultiplyColor(origColor);
+    size_t dstOffset = dstRB - (width << 2);
+    size_t maskOffset = maskRB - width;
+    SkPMColor* dst = (SkPMColor *)device;
+    const uint8_t* mask = (const uint8_t*)maskPtr;
+    do {
+        int count = width;
+        if (count >= 4) {
+            while (((size_t)dst & 0x0F) != 0 && (count > 0)) {
+                *dst = SkBlendARGB32(color, *dst, *mask);
+                mask++;
+                dst++;
+                count--;
+            }
+            __m128i *d = reinterpret_cast<__m128i*>(dst);
+            __m128i rb_mask = _mm_set1_epi32(0x00FF00FF);
+            __m128i c_256 = _mm_set1_epi16(256);
+            __m128i c_1 = _mm_set1_epi16(1);
+            __m128i src_pixel = _mm_set1_epi32(color);
+            while (count >= 4) {
+                // Load 4 pixels each of src and dest.
+                __m128i dst_pixel = _mm_load_si128(d);
+
+                //set the aphla value
+                __m128i src_scale_wide =  _mm_set_epi8(0, *(mask+3),\
+                                0, *(mask+3),0, \
+                                *(mask+2),0, *(mask+2),\
+                                0,*(mask+1), 0,*(mask+1),\
+                                0, *mask,0,*mask);
+
+                //call SkAlpha255To256()
+                src_scale_wide = _mm_add_epi16(src_scale_wide, c_1);
+
+                // Get red and blue pixels into lower byte of each word.
+                __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel);
+                __m128i src_rb = _mm_and_si128(rb_mask, src_pixel);
+
+                // Get alpha and green into lower byte of each word.
+                __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8);
+                __m128i src_ag = _mm_srli_epi16(src_pixel, 8);
+
+                // Put per-pixel alpha in low byte of each word.
+                __m128i dst_alpha = _mm_shufflehi_epi16(src_ag, 0xF5);
+                dst_alpha = _mm_shufflelo_epi16(dst_alpha, 0xF5);
+
+                // dst_alpha = dst_alpha * src_scale
+                dst_alpha = _mm_mullo_epi16(dst_alpha, src_scale_wide);
+
+                // Divide by 256.
+                dst_alpha = _mm_srli_epi16(dst_alpha, 8);
+
+                // Subtract alphas from 256, to get 1..256
+                dst_alpha = _mm_sub_epi16(c_256, dst_alpha);
+                // Multiply red and blue by dst pixel alpha.
+                dst_rb = _mm_mullo_epi16(dst_rb, dst_alpha);
+                // Multiply alpha and green by dst pixel alpha.
+                dst_ag = _mm_mullo_epi16(dst_ag, dst_alpha);
+
+                // Multiply red and blue by global alpha.
+                src_rb = _mm_mullo_epi16(src_rb, src_scale_wide);
+                // Multiply alpha and green by global alpha.
+                src_ag = _mm_mullo_epi16(src_ag, src_scale_wide);
+                // Divide by 256.
+                dst_rb = _mm_srli_epi16(dst_rb, 8);
+                src_rb = _mm_srli_epi16(src_rb, 8);
+
+                // Mask out low bits (goodies already in the right place; no need to divide)
+                dst_ag = _mm_andnot_si128(rb_mask, dst_ag);
+                src_ag = _mm_andnot_si128(rb_mask, src_ag);
+
+                // Combine back into RGBA.
+                dst_pixel = _mm_or_si128(dst_rb, dst_ag);
+                __m128i tmp_src_pixel = _mm_or_si128(src_rb, src_ag);
+
+                // Add two pixels into result.
+                __m128i result = _mm_add_epi8(tmp_src_pixel, dst_pixel);
+                _mm_store_si128(d, result);
+                // load the next 4 pixel
+                mask = mask + 4;
+                d++;
+                count -= 4;
+            }
+            dst = reinterpret_cast<SkPMColor *>(d);
+        }
+        while(count > 0) {
+            *dst= SkBlendARGB32(color, *dst, *mask);
+            dst += 1;
+            mask++;
+            count --;
+        }
+        dst = (SkPMColor *)((char*)dst + dstOffset);
+        mask += maskOffset;
+    } while (--height != 0);
+}
+
+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 g = _mm_and_si128(_mm_slli_epi32(mask,
+                              8-SK_G16_SHIFT-(SK_G16_BITS-5)),
+                              _mm_set1_epi32(0x00001F00));
+
+    __m128i b = _mm_and_si128(_mm_slli_epi32(mask,
+                              SK_B16_BITS-5),
+                              _mm_set1_epi32(0x0000001F));
+            
+    // 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. 
+    __m128i maskLo, maskHi;
+    maskLo = _mm_unpacklo_epi8(mask, _mm_setzero_si128());
+    maskHi = _mm_unpackhi_epi8(mask, _mm_setzero_si128());
+
+    // Upscale to 0..32
+    maskLo = _mm_add_epi16(maskLo, _mm_srli_epi16(maskLo, 4));
+    maskHi = _mm_add_epi16(maskHi, _mm_srli_epi16(maskHi, 4));
+
+    maskLo = _mm_mullo_epi16(maskLo, scale);
+    maskHi = _mm_mullo_epi16(maskHi, scale);
+
+    maskLo = _mm_srli_epi16(maskLo, 8);
+    maskHi = _mm_srli_epi16(maskHi, 8);
+
+    // Interleave R,G,B into the lower byte of the word.
+    __m128i dstLo = _mm_unpacklo_epi8(dst, _mm_setzero_si128());
+    __m128i dstHi = _mm_unpackhi_epi8(dst, _mm_setzero_si128());
+
+    maskLo = _mm_mullo_epi16(maskLo, _mm_sub_epi16(srci, dstLo));
+    maskHi = _mm_mullo_epi16(maskHi, _mm_sub_epi16(srci, dstHi));
+
+    maskLo = _mm_srai_epi16(maskLo, 5);
+    maskHi = _mm_srai_epi16(maskHi, 5);
+
+    // Add two pixels into result.
+    __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);
+}
+
+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 g = _mm_and_si128(_mm_slli_epi32(mask,
+                              8-SK_G16_SHIFT-(SK_G16_BITS-5)),
+                              _mm_set1_epi32(0x00001F00));
+
+    __m128i b = _mm_and_si128(_mm_slli_epi32(mask, SK_B16_BITS-5),
+                              _mm_set1_epi32(0x0000001F));
+            
+    // 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. 
+    __m128i maskLo, maskHi;
+    maskLo = _mm_unpacklo_epi8(mask, _mm_setzero_si128());
+    maskHi = _mm_unpackhi_epi8(mask, _mm_setzero_si128());
+
+    // Upscale to 0..32
+    maskLo = _mm_add_epi16(maskLo, _mm_srli_epi16(maskLo, 4));
+    maskHi = _mm_add_epi16(maskHi, _mm_srli_epi16(maskHi, 4));
+
+    // Interleave R,G,B into the lower byte of the word.
+    __m128i dstLo = _mm_unpacklo_epi8(dst, _mm_setzero_si128());
+    __m128i dstHi = _mm_unpackhi_epi8(dst, _mm_setzero_si128());
+
+    maskLo = _mm_mullo_epi16(maskLo, _mm_sub_epi16(srci, dstLo));
+    maskHi = _mm_mullo_epi16(maskHi, _mm_sub_epi16(srci, dstHi));
+
+    maskLo = _mm_srai_epi16(maskLo, 5);
+    maskHi = _mm_srai_epi16(maskHi, 5);
+
+    // Add two pixels into result.
+    __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);
+}
+
+void SkBlitLCD16Row_SSE2(SkPMColor dst[], const uint16_t src[],
+                         SkColor color, int width, SkPMColor) {
+    if (width <= 0) {
+        return;
+    }
+
+    int srcA = SkColorGetA(color);
+    int srcR = SkColorGetR(color);
+    int srcG = SkColorGetG(color);
+    int srcB = SkColorGetB(color);
+    
+    srcA = SkAlpha255To256(srcA);
+
+    if (width >= 4) {
+        SkASSERT(((size_t)dst & 0x03) == 0);
+        while (((size_t)dst & 0x0F) != 0) {
+            *dst = SkBlendLCD16(srcA, srcR, srcG, srcB, *dst, *src);
+            src++;
+            dst++;
+            width--;
+        }
+
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+        __m128i srci = _mm_set1_epi32(SkPackARGB32(0xFF, srcR, srcG, srcB));
+        srci = _mm_unpacklo_epi8(srci, _mm_setzero_si128());
+        __m128i scale = _mm_set1_epi16(srcA);
+        while (width >= 4) {
+            __m128i dst_pixel = _mm_load_si128(d);
+            __m128i mask_pixel = _mm_loadl_epi64(
+                                     reinterpret_cast<const __m128i*>(src));
+
+            // Check whether mask_pixels are equal to 0 and get the highest bit
+            // of each byte of result, if mask pixes are all zero, we will get
+            // pack_cmp to 0xFFFF
+            int pack_cmp = _mm_movemask_epi8(_mm_cmpeq_epi16(mask_pixel,
+                                             _mm_setzero_si128()));
+
+            // if mask pixels are not all zero, we will blend the dst pixels
+            if (pack_cmp != 0xFFFF) {
+                // 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); 
+                _mm_store_si128(d, result);
+            }
+
+            d++;
+            src += 4;
+            width -= 4;
+        }
+
+        dst = reinterpret_cast<SkPMColor*>(d);
+    }
+
+    while (width > 0) {
+        *dst = SkBlendLCD16(srcA, srcR, srcG, srcB, *dst, *src);
+        src++;
+        dst++;
+        width--;        
+    }
+}
+
+void SkBlitLCD16OpaqueRow_SSE2(SkPMColor dst[], const uint16_t src[],
+                               SkColor color, int width, SkPMColor opaqueDst) {
+    if (width <= 0) {
+        return;
+    }
+
+    int srcR = SkColorGetR(color);
+    int srcG = SkColorGetG(color);
+    int srcB = SkColorGetB(color);
+
+    if (width >= 4) {
+        SkASSERT(((size_t)dst & 0x03) == 0);
+        while (((size_t)dst & 0x0F) != 0) {
+            *dst = SkBlendLCD16Opaque(srcR, srcG, srcB, *dst, *src, opaqueDst);
+            src++;
+            dst++;
+            width--;
+        }
+
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+        __m128i srci = _mm_set1_epi32(SkPackARGB32(0xFF, srcR, srcG, srcB));
+        srci = _mm_unpacklo_epi8(srci, _mm_setzero_si128());
+        while (width >= 4) {
+            __m128i dst_pixel = _mm_load_si128(d);
+            __m128i mask_pixel = _mm_loadl_epi64(
+                                     reinterpret_cast<const __m128i*>(src));
+
+            // Check whether mask_pixels are equal to 0 and get the highest bit
+            // of each byte of result, if mask pixes are all zero, we will get
+            // pack_cmp to 0xFFFF
+            int pack_cmp = _mm_movemask_epi8(_mm_cmpeq_epi16(mask_pixel,
+                                             _mm_setzero_si128()));
+
+            // if mask pixels are not all zero, we will blend the dst pixels
+            if (pack_cmp != 0xFFFF) {
+                // 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); 
+                _mm_store_si128(d, result);
+            }
+
+            d++;
+            src += 4;
+            width -= 4;
+        }
+
+        dst = reinterpret_cast<SkPMColor*>(d);
+    }
+
+    while (width > 0) {
+        *dst = SkBlendLCD16Opaque(srcR, srcG, srcB, *dst, *src, opaqueDst);
+        src++;
+        dst++;
+        width--;        
+    }
+}
diff --git a/legacy/src/opts/SkBlitRow_opts_SSE2.h b/legacy/src/opts/SkBlitRow_opts_SSE2.h
new file mode 100644
index 0000000..b443ec7
--- /dev/null
+++ b/legacy/src/opts/SkBlitRow_opts_SSE2.h
@@ -0,0 +1,30 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkBlitRow.h"
+
+void S32_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src,
+                              int count, U8CPU alpha);
+
+void S32A_Opaque_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
+                                const SkPMColor* SK_RESTRICT src,
+                                int count, U8CPU alpha);
+
+void S32A_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
+                               const SkPMColor* SK_RESTRICT src,
+                               int count, U8CPU alpha);
+void SkARGB32_A8_BlitMask_SSE2(void* device, size_t dstRB, const void* mask,
+                               size_t maskRB, SkColor color,
+                               int width, int height);
+
+void SkBlitLCD16Row_SSE2(SkPMColor dst[], const uint16_t src[],
+                         SkColor color, int width, SkPMColor);
+void SkBlitLCD16OpaqueRow_SSE2(SkPMColor dst[], const uint16_t src[],
+                               SkColor color, int width, SkPMColor opaqueDst);
diff --git a/legacy/src/opts/SkBlitRow_opts_arm.cpp b/legacy/src/opts/SkBlitRow_opts_arm.cpp
new file mode 100644
index 0000000..dd8e406
--- /dev/null
+++ b/legacy/src/opts/SkBlitRow_opts_arm.cpp
@@ -0,0 +1,1871 @@
+/*
+ * 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.
+ */
+
+
+#include "SkBlitRow.h"
+#include "SkBlitMask.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+#if defined(__ARM_HAVE_NEON)
+#include <arm_neon.h>
+#endif
+
+#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 (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*/) {
+    SkASSERT(255 == alpha);
+
+    asm volatile (
+                  "1:                                   \n\t"
+                  "ldr     r3, [%[src]], #4             \n\t"
+                  "cmp     r3, #0xff000000              \n\t"
+                  "blo     2f                           \n\t"
+                  "and     r4, r3, #0x0000f8            \n\t"
+                  "and     r5, r3, #0x00fc00            \n\t"
+                  "and     r6, r3, #0xf80000            \n\t"
+                  "pld     [r1, #32]                    \n\t"
+                  "lsl     r3, r4, #8                   \n\t"
+                  "orr     r3, r3, r5, lsr #5           \n\t"
+                  "orr     r3, r3, r6, lsr #19          \n\t"
+                  "subs    %[count], %[count], #1       \n\t"
+                  "strh    r3, [%[dst]], #2             \n\t"
+                  "bne     1b                           \n\t"
+                  "b       4f                           \n\t"
+                  "2:                                   \n\t"
+                  "lsrs    r7, r3, #24                  \n\t"
+                  "beq     3f                           \n\t"
+                  "ldrh    r4, [%[dst]]                 \n\t"
+                  "rsb     r7, r7, #255                 \n\t"
+                  "and     r6, r4, #0x001f              \n\t"
+                  "ubfx    r5, r4, #5, #6               \n\t"
+                  "pld     [r0, #16]                    \n\t"
+                  "lsr     r4, r4, #11                  \n\t"
+                  "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"
+                  "and     r3, r3, #0xff                \n\t"
+                  "add     r6, r6, #16                  \n\t"
+                  "add     r5, r5, #32                  \n\t"
+                  "add     r4, r4, #16                  \n\t"
+                  "add     r6, r6, r6, lsr #5           \n\t"
+                  "add     r5, r5, r5, lsr #6           \n\t"
+                  "add     r4, r4, r4, lsr #5           \n\t"
+                  "add     r6, r7, r6, lsr #5           \n\t"
+                  "add     r5, ip, r5, lsr #6           \n\t"
+                  "add     r4, r3, r4, lsr #5           \n\t"
+                  "lsr     r6, r6, #3                   \n\t"
+                  "and     r5, r5, #0xfc                \n\t"
+                  "and     r4, r4, #0xf8                \n\t"
+                  "orr     r6, r6, r5, lsl #3           \n\t"
+                  "orr     r4, r6, r4, lsl #8           \n\t"
+                  "strh    r4, [%[dst]], #2             \n\t"
+                  "pld     [r1, #32]                    \n\t"
+                  "subs    %[count], %[count], #1       \n\t"
+                  "bne     1b                           \n\t"
+                  "b       4f                           \n\t"
+                  "3:                                   \n\t"
+                  "subs    %[count], %[count], #1       \n\t"
+                  "add     %[dst], %[dst], #2           \n\t"
+                  "bne     1b                           \n\t"
+                  "4:                                   \n\t"
+                  : [dst] "+r" (dst), [src] "+r" (src), [count] "+r" (count)
+                  :
+                  : "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,
+                                  int count, U8CPU alpha) {
+
+    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 */
+
+                  "mov    ip, #0xff                  \n\t" /* load the 0xff mask in ip */
+                  "orr    ip, ip, ip, lsl #16        \n\t" /* convert it to 0xff00ff in ip */
+
+                  "cmp    %[count], #2               \n\t" /* compare count with 2 */
+                  "blt    2f                         \n\t" /* if less than 2 -> single loop */
+
+                  /* Double Loop */
+                  "1:                                \n\t" /* <double loop> */
+                  "ldm    %[src]!, {r5,r6}           \n\t" /* load the src(s) at r5-r6 */
+                  "ldm    %[dst], {r7,r8}            \n\t" /* loading dst(s) into r7-r8 */
+                  "lsr    r4, r5, #24                \n\t" /* extracting the alpha from source and storing it to r4 */
+
+                  /* ----------- */
+                  "and    r9, ip, r7                 \n\t" /* r9 = br masked by ip */
+                  "rsb    r4, r4, #256               \n\t" /* subtracting the alpha from 256 -> r4=scale */
+                  "and    r10, ip, r7, lsr #8        \n\t" /* r10 = ag masked by ip */
+
+                  "mul    r9, r9, r4                 \n\t" /* br = br * scale */
+                  "mul    r10, r10, r4               \n\t" /* ag = ag * scale */
+                  "and    r9, ip, r9, lsr #8         \n\t" /* lsr br by 8 and mask it */
+
+                  "and    r10, r10, ip, lsl #8       \n\t" /* mask ag with reverse mask */
+                  "lsr    r4, r6, #24                \n\t" /* extracting the alpha from source and storing it to r4 */
+                  "orr    r7, r9, r10                \n\t" /* br | ag*/
+
+                  "add    r7, r5, r7                 \n\t" /* dst = src + calc dest(r7) */
+                  "rsb    r4, r4, #256               \n\t" /* subtracting the alpha from 255 -> r4=scale */
+
+                  /* ----------- */
+                  "and    r9, ip, r8                 \n\t" /* r9 = br masked by ip */
+
+                  "and    r10, ip, r8, lsr #8        \n\t" /* r10 = ag masked by ip */
+                  "mul    r9, r9, r4                 \n\t" /* br = br * scale */
+                  "sub    %[count], %[count], #2     \n\t"
+                  "mul    r10, r10, r4               \n\t" /* ag = ag * scale */
+
+                  "and    r9, ip, r9, lsr #8         \n\t" /* lsr br by 8 and mask it */
+                  "and    r10, r10, ip, lsl #8       \n\t" /* mask ag with reverse mask */
+                  "cmp    %[count], #1               \n\t" /* comparing count with 1 */
+                  "orr    r8, r9, r10                \n\t" /* br | ag */
+
+                  "add    r8, r6, r8                 \n\t" /* dst = src + calc dest(r8) */
+
+                  /* ----------------- */
+                  "stm    %[dst]!, {r7,r8}           \n\t" /* *dst = r7, increment dst by two (each times 4) */
+                  /* ----------------- */
+
+                  "bgt    1b                         \n\t" /* if greater than 1 -> reloop */
+                  "blt    3f                         \n\t" /* if less than 1 -> exit */
+
+                  /* Single Loop */
+                  "2:                                \n\t" /* <single loop> */
+                  "ldr    r5, [%[src]], #4           \n\t" /* load the src pointer into r5 r5=src */
+                  "ldr    r7, [%[dst]]               \n\t" /* loading dst into r7 */
+                  "lsr    r4, r5, #24                \n\t" /* extracting the alpha from source and storing it to r4 */
+
+                  /* ----------- */
+                  "and    r9, ip, r7                 \n\t" /* r9 = br masked by ip */
+                  "rsb    r4, r4, #256               \n\t" /* subtracting the alpha from 256 -> r4=scale */
+
+                  "and    r10, ip, r7, lsr #8        \n\t" /* r10 = ag masked by ip */
+                  "mul    r9, r9, r4                 \n\t" /* br = br * scale */
+                  "mul    r10, r10, r4               \n\t" /* ag = ag * scale */
+                  "and    r9, ip, r9, lsr #8         \n\t" /* lsr br by 8 and mask it */
+
+                  "and    r10, r10, ip, lsl #8       \n\t" /* mask ag */
+                  "orr    r7, r9, r10                \n\t" /* br | ag */
+
+                  "add    r7, r5, r7                 \n\t" /* *dst = src + calc dest(r7) */
+
+                  /* ----------------- */
+                  "str    r7, [%[dst]], #4           \n\t" /* *dst = r7, increment dst by one (times 4) */
+                  /* ----------------- */
+
+                  "3:                                \n\t" /* <exit> */
+                  : [dst] "+r" (dst), [src] "+r" (src), [count] "+r" (count)
+                  :
+                  : "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
+
+/*
+ * 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) {
+    asm volatile (
+                  "cmp    %[count], #0               \n\t" /* comparing count with 0 */
+                  "beq    3f                         \n\t" /* if zero exit */
+
+                  "mov    r12, #0xff                 \n\t" /* load the 0xff mask in r12 */
+                  "orr    r12, r12, r12, lsl #16     \n\t" /* convert it to 0xff00ff in r12 */
+
+                  /* src1,2_scale */
+                  "add    %[alpha], %[alpha], #1     \n\t" /* loading %[alpha]=src_scale=alpha+1 */
+
+                  "cmp    %[count], #2               \n\t" /* comparing count with 2 */
+                  "blt    2f                         \n\t" /* if less than 2 -> single loop */
+
+                  /* Double Loop */
+                  "1:                                \n\t" /* <double loop> */
+                  "ldm    %[src]!, {r5, r6}          \n\t" /* loading src pointers into r5 and r6 */
+                  "ldm    %[dst], {r7, r8}           \n\t" /* loading dst pointers into r7 and r8 */
+
+                  /* dst1_scale and dst2_scale*/
+                  "lsr    r9, r5, #24                \n\t" /* src >> 24 */
+                  "lsr    r10, r6, #24               \n\t" /* src >> 24 */
+                  "smulbb r9, r9, %[alpha]           \n\t" /* r9 = SkMulS16 r9 with src_scale */
+                  "smulbb r10, r10, %[alpha]         \n\t" /* r10 = SkMulS16 r10 with src_scale */
+                  "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 */
+                  "rsb    r10, r10, #256             \n\t" /* dst2_scale = r10 = 255 - r10 + 1 */
+
+                  /* ---------------------- */
+
+                  /* src1, src1_scale */
+                  "and    r11, r12, r5, lsr #8       \n\t" /* ag = r11 = r5 masked by r12 lsr by #8 */
+                  "and    r4, r12, r5                \n\t" /* rb = r4 = r5 masked by r12 */
+                  "mul    r11, r11, %[alpha]         \n\t" /* ag = r11 times src_scale */
+                  "mul    r4, r4, %[alpha]           \n\t" /* rb = r4 times src_scale */
+                  "and    r11, r11, r12, lsl #8      \n\t" /* ag masked by reverse mask (r12) */
+                  "and    r4, r12, r4, lsr #8        \n\t" /* rb masked by mask (r12) */
+                  "orr    r5, r11, r4                \n\t" /* r5 = (src1, src_scale) */
+
+                  /* dst1, dst1_scale */
+                  "and    r11, r12, r7, lsr #8       \n\t" /* ag = r11 = r7 masked by r12 lsr by #8 */
+                  "and    r4, r12, r7                \n\t" /* rb = r4 = r7 masked by r12 */
+                  "mul    r11, r11, r9               \n\t" /* ag = r11 times dst_scale (r9) */
+                  "mul    r4, r4, r9                 \n\t" /* rb = r4 times dst_scale (r9) */
+                  "and    r11, r11, r12, lsl #8      \n\t" /* ag masked by reverse mask (r12) */
+                  "and    r4, r12, r4, lsr #8        \n\t" /* rb masked by mask (r12) */
+                  "orr    r9, r11, r4                \n\t" /* r9 = (dst1, dst_scale) */
+
+                  /* ---------------------- */
+                  "add    r9, r5, r9                 \n\t" /* *dst = src plus dst both scaled */
+                  /* ---------------------- */
+
+                  /* ====================== */
+
+                  /* src2, src2_scale */
+                  "and    r11, r12, r6, lsr #8       \n\t" /* ag = r11 = r6 masked by r12 lsr by #8 */
+                  "and    r4, r12, r6                \n\t" /* rb = r4 = r6 masked by r12 */
+                  "mul    r11, r11, %[alpha]         \n\t" /* ag = r11 times src_scale */
+                  "mul    r4, r4, %[alpha]           \n\t" /* rb = r4 times src_scale */
+                  "and    r11, r11, r12, lsl #8      \n\t" /* ag masked by reverse mask (r12) */
+                  "and    r4, r12, r4, lsr #8        \n\t" /* rb masked by mask (r12) */
+                  "orr    r6, r11, r4                \n\t" /* r6 = (src2, src_scale) */
+
+                  /* dst2, dst2_scale */
+                  "and    r11, r12, r8, lsr #8       \n\t" /* ag = r11 = r8 masked by r12 lsr by #8 */
+                  "and    r4, r12, r8                \n\t" /* rb = r4 = r8 masked by r12 */
+                  "mul    r11, r11, r10              \n\t" /* ag = r11 times dst_scale (r10) */
+                  "mul    r4, r4, r10                \n\t" /* rb = r4 times dst_scale (r6) */
+                  "and    r11, r11, r12, lsl #8      \n\t" /* ag masked by reverse mask (r12) */
+                  "and    r4, r12, r4, lsr #8        \n\t" /* rb masked by mask (r12) */
+                  "orr    r10, r11, r4               \n\t" /* r10 = (dst2, dst_scale) */
+
+                  "sub    %[count], %[count], #2     \n\t" /* decrease count by 2 */
+                  /* ---------------------- */
+                  "add    r10, r6, r10               \n\t" /* *dst = src plus dst both scaled */
+                  /* ---------------------- */
+                  "cmp    %[count], #1               \n\t" /* compare count with 1 */
+                  /* ----------------- */
+                  "stm    %[dst]!, {r9, r10}         \n\t" /* copy r9 and r10 to r7 and r8 respectively */
+                  /* ----------------- */
+
+                  "bgt    1b                         \n\t" /* if %[count] greater than 1 reloop */
+                  "blt    3f                         \n\t" /* if %[count] less than 1 exit */
+                                                           /* else get into the single loop */
+                  /* Single Loop */
+                  "2:                                \n\t" /* <single loop> */
+                  "ldr    r5, [%[src]], #4           \n\t" /* loading src pointer into r5: r5=src */
+                  "ldr    r7, [%[dst]]               \n\t" /* loading dst pointer into r7: r7=dst */
+
+                  "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 */
+                  "smulbb r6, r6, %[alpha]           \n\t" /* r6 = SkMulS16 with src_scale */
+                  "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 */
+                  "rsb    r6, r6, #256               \n\t" /* r6 = 255 - r6 + 1 */
+
+                  /* src, src_scale */
+                  "mul    r9, r9, %[alpha]           \n\t" /* rb = r9 times scale */
+                  "and    r8, r8, r12, lsl #8        \n\t" /* ag masked by reverse mask (r12) */
+                  "and    r9, r12, r9, lsr #8        \n\t" /* rb masked by mask (r12) */
+                  "orr    r10, r8, r9                \n\t" /* r10 = (scr, src_scale) */
+
+                  /* dst, dst_scale */
+                  "and    r8, r12, r7, lsr #8        \n\t" /* ag = r8 = r7 masked by r12 lsr by #8 */
+                  "and    r9, r12, r7                \n\t" /* rb = r9 = r7 masked by r12 */
+                  "mul    r8, r8, r6                 \n\t" /* ag = r8 times scale (r6) */
+                  "mul    r9, r9, r6                 \n\t" /* rb = r9 times scale (r6) */
+                  "and    r8, r8, r12, lsl #8        \n\t" /* ag masked by reverse mask (r12) */
+                  "and    r9, r12, r9, lsr #8        \n\t" /* rb masked by mask (r12) */
+                  "orr    r7, r8, r9                 \n\t" /* r7 = (dst, dst_scale) */
+
+                  "add    r10, r7, r10               \n\t" /* *dst = src plus dst both scaled */
+
+                  /* ----------------- */
+                  "str    r10, [%[dst]], #4          \n\t" /* *dst = r10, postincrement dst by one (times 4) */
+                  /* ----------------- */
+
+                  "3:                                \n\t" /* <exit> */
+                  : [dst] "+r" (dst), [src] "+r" (src), [count] "+r" (count), [alpha] "+r" (alpha)
+                  :
+                  : "cc", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "memory"
+                  );
+
+}
+#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[] = {
+    // no dither
+    S32_D565_Opaque_PROC,
+    S32_D565_Blend_PROC,
+    S32A_D565_Opaque_PROC,
+    S32A_D565_Blend_PROC,
+    
+    // dither
+    S32_D565_Opaque_Dither_PROC,
+    S32_D565_Blend_Dither_PROC,
+    S32A_D565_Opaque_Dither_PROC,
+    NULL,   // S32A_D565_Blend_Dither
+};
+
+static const SkBlitRow::Proc platform_4444_procs[] = {
+    // 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
+};
+
+static const SkBlitRow::Proc32 platform_32_procs[] = {
+    NULL,   // S32_Opaque,
+    S32_Blend_BlitRow32_PROC,		// S32_Blend,
+    S32A_Opaque_BlitRow32_PROC,		// S32A_Opaque,
+    S32A_Blend_BlitRow32_PROC		// S32A_Blend
+};
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs4444(unsigned flags) {
+    return platform_4444_procs[flags];
+}
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs565(unsigned flags) {
+    return platform_565_procs[flags];
+}
+
+SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
+    return platform_32_procs[flags];
+}
+
+SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlitMask::ColorProc SkBlitMask::PlatformColorProcs(SkBitmap::Config dstConfig,
+                                                     SkMask::Format maskFormat,
+                                                     SkColor color) {
+    return NULL;
+}
+
+SkBlitMask::BlitLCD16RowProc SkBlitMask::PlatformBlitRowProcs16(bool isOpaque) {
+    return NULL;
+}
+
+SkBlitMask::RowProc SkBlitMask::PlatformRowProcs(SkBitmap::Config dstConfig,
+                                                 SkMask::Format maskFormat,
+                                                 RowFlags flags) {
+    return NULL;
+}
diff --git a/legacy/src/opts/SkBlitRow_opts_none.cpp b/legacy/src/opts/SkBlitRow_opts_none.cpp
new file mode 100644
index 0000000..5f4598e
--- /dev/null
+++ b/legacy/src/opts/SkBlitRow_opts_none.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBlitRow.h"
+#include "SkBlitMask.h"
+
+// Platform impl of Platform_procs with no overrides
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs4444(unsigned flags) {
+    return NULL;
+}
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs565(unsigned flags) {
+    return NULL;
+}
+
+SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
+    return NULL;
+}
+
+SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlitMask::ColorProc SkBlitMask::PlatformColorProcs(SkBitmap::Config dstConfig,
+                                                     SkMask::Format maskFormat,
+                                                     SkColor color) {
+    return NULL;
+}
+
+SkBlitMask::BlitLCD16RowProc SkBlitMask::PlatformBlitRowProcs16(bool isOpaque) {
+    return NULL;
+}
+
+SkBlitMask::RowProc SkBlitMask::PlatformRowProcs(SkBitmap::Config dstConfig,
+                                                 SkMask::Format maskFormat,
+                                                 RowFlags flags) {
+    return NULL;
+}
+
diff --git a/legacy/src/opts/SkUtils_opts_SSE2.cpp b/legacy/src/opts/SkUtils_opts_SSE2.cpp
new file mode 100644
index 0000000..63e7f2c
--- /dev/null
+++ b/legacy/src/opts/SkUtils_opts_SSE2.cpp
@@ -0,0 +1,69 @@
+
+/*
+ * 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.
+ */
+
+
+#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);
+
+    // dst must be 2-byte aligned.
+    SkASSERT((((size_t) dst) & 0x01) == 0);
+
+    if (count >= 32) {
+        while (((size_t)dst) & 0x0F) {
+            *dst++ = value;
+            --count;
+        }
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+        __m128i value_wide = _mm_set1_epi16(value);
+        while (count >= 32) {
+            _mm_store_si128(d++, value_wide);
+            _mm_store_si128(d++, value_wide);
+            _mm_store_si128(d++, value_wide);
+            _mm_store_si128(d++, value_wide);
+            count -= 32;
+        }
+        dst = reinterpret_cast<uint16_t*>(d);
+    }
+    while (count > 0) {
+        *dst++ = value;
+        --count;
+    }
+}
+ 
+void sk_memset32_SSE2(uint32_t *dst, uint32_t value, int count)
+{
+    SkASSERT(dst != NULL && count >= 0);
+
+    // dst must be 4-byte aligned.
+    SkASSERT((((size_t) dst) & 0x03) == 0);
+
+    if (count >= 16) {
+        while (((size_t)dst) & 0x0F) {
+            *dst++ = value;
+            --count;
+        }
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+        __m128i value_wide = _mm_set1_epi32(value);
+        while (count >= 16) {
+            _mm_store_si128(d++, value_wide);
+            _mm_store_si128(d++, value_wide);
+            _mm_store_si128(d++, value_wide);
+            _mm_store_si128(d++, value_wide);
+            count -= 16;
+        }
+        dst = reinterpret_cast<uint32_t*>(d);
+    }
+    while (count > 0) {
+        *dst++ = value;
+        --count;
+    }
+}
diff --git a/legacy/src/opts/SkUtils_opts_SSE2.h b/legacy/src/opts/SkUtils_opts_SSE2.h
new file mode 100644
index 0000000..771656f
--- /dev/null
+++ b/legacy/src/opts/SkUtils_opts_SSE2.h
@@ -0,0 +1,13 @@
+
+/*
+ * 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.
+ */
+
+
+#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/legacy/src/opts/SkUtils_opts_none.cpp b/legacy/src/opts/SkUtils_opts_none.cpp
new file mode 100644
index 0000000..286f10d
--- /dev/null
+++ b/legacy/src/opts/SkUtils_opts_none.cpp
@@ -0,0 +1,18 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkUtils.h"
+
+SkMemset16Proc SkMemset16GetPlatformProc() {
+    return NULL;
+}
+
+SkMemset32Proc SkMemset32GetPlatformProc() {
+    return NULL;
+}
diff --git a/legacy/src/opts/memset.arm.S b/legacy/src/opts/memset.arm.S
new file mode 100644
index 0000000..1248631
--- /dev/null
+++ b/legacy/src/opts/memset.arm.S
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Changes:
+ * 2010-08-11 Steve McIntyre <steve.mcintyre@arm.com>
+ *    Added small changes to the two functions to make them work on the
+ *    specified number of 16- or 32-bit values rather than the original
+ *    code which was specified as a count of bytes. More verbose comments
+ *    to aid future maintenance.
+ */
+
+    .text
+    .align
+
+    .global arm_memset32
+    .type   arm_memset32, %function
+    .global arm_memset16
+    .type   arm_memset16, %function
+
+/*
+ * Optimized memset functions for ARM.
+ *
+ * void arm_memset16(uint16_t* dst, uint16_t value, int count);
+ * void arm_memset32(uint32_t* dst, uint32_t value, int count);
+ *
+ */
+arm_memset16:
+        .fnstart
+        push        {lr}
+
+        /* if count is equal to zero then abort */
+        teq         r2, #0
+        ble         .Lfinish
+
+        /* Multiply count by 2 - go from the number of 16-bit shorts
+         * to the number of bytes desired. */
+        mov         r2, r2, lsl #1
+
+        /* expand the data to 32 bits */
+        orr         r1, r1, lsl #16
+
+        /* align to 32 bits */
+        tst         r0, #2
+        strneh      r1, [r0], #2
+        subne       r2, r2, #2
+
+        /* Now jump into the main loop below. */
+        b           .Lwork_32
+        .fnend
+
+arm_memset32:
+        .fnstart
+        push        {lr}
+
+        /* if count is equal to zero then abort */
+        teq         r2, #0
+        ble         .Lfinish
+
+        /* Multiply count by 4 - go from the number of 32-bit words to
+         * the number of bytes desired. */
+        mov         r2, r2, lsl #2
+
+.Lwork_32:
+        /* Set up registers ready for writing them out. */
+        mov         ip, r1
+        mov         lr, r1
+
+        /* Try to align the destination to a cache line. Assume 32
+         * byte (8 word) cache lines, it's the common case. */
+        rsb         r3, r0, #0
+        ands        r3, r3, #0x1C
+        beq         .Laligned32
+        cmp         r3, r2
+        andhi       r3, r2, #0x1C
+        sub         r2, r2, r3
+
+        /* (Optionally) write any unaligned leading bytes.
+         * (0-28 bytes, length in r3) */
+        movs        r3, r3, lsl #28
+        stmcsia     r0!, {r1, lr}
+        stmcsia     r0!, {r1, lr}
+        stmmiia     r0!, {r1, lr}
+        movs        r3, r3, lsl #2
+        strcs       r1, [r0], #4
+
+        /* Now quickly loop through the cache-aligned data. */
+.Laligned32:
+        mov         r3, r1
+1:      subs        r2, r2, #32
+        stmhsia     r0!, {r1,r3,ip,lr}
+        stmhsia     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}
+        movs        r2, r2, lsl #2
+        strcs       r1, [r0], #4
+        strmih      lr, [r0], #2
+
+.Lfinish:
+        pop         {pc}
+        .fnend
diff --git a/legacy/src/opts/memset16_neon.S b/legacy/src/opts/memset16_neon.S
new file mode 100644
index 0000000..b1719fa
--- /dev/null
+++ b/legacy/src/opts/memset16_neon.S
@@ -0,0 +1,143 @@
+/***************************************************************************
+ * Copyright (c) 2009,2010, Code Aurora Forum. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ ***************************************************************************/
+
+/***************************************************************************
+  Neon memset: Attempts to do a memset with Neon registers if possible,
+     Inputs:
+        s: The buffer to write to
+        c: The integer data to write to the buffer
+        n: The size_t count.
+     Outputs:
+
+***************************************************************************/
+
+        .code 32
+        .fpu neon
+        .align 4
+        .globl memset16_neon
+        .func
+
+memset16_neon:
+        cmp             r2, #0
+        bxeq            lr
+
+        /* Keep in mind that r2 -- the count argument -- is for the
+         * number of 16-bit items to copy.
+         */
+        lsl             r2, r2, #1
+
+        push            {r0}
+
+        /* If we have < 8 bytes, just do a quick loop to handle that */
+        cmp             r2, #8
+        bgt             memset_gt4
+memset_smallcopy_loop:
+        strh            r1, [r0], #2
+        subs            r2, r2, #2
+        bne             memset_smallcopy_loop
+memset_smallcopy_done:
+        pop             {r0}
+        bx              lr
+
+memset_gt4:
+        /*
+         * Duplicate the r1 lowest 16-bits across r1. The idea is to have
+         * a register with two 16-bit-values we can copy. We do this by
+         * duplicating lowest 16-bits of r1 to upper 16-bits.
+         */
+        orr             r1, r1, r1, lsl #16
+        /*
+         * If we're copying > 64 bytes, then we may want to get
+         * onto a 16-byte boundary to improve speed even more.
+         */
+        cmp             r2, #64
+        blt             memset_route
+        ands            r12, r0, #0xf
+        beq             memset_route
+        /*
+         * Determine the number of bytes to move forward to get to the 16-byte
+         * boundary.  Note that this will be a multiple of 4, since we
+         * already are word-aligned.
+         */
+        rsb             r12, r12, #16
+        sub             r2, r2, r12
+        lsls            r12, r12, #29
+        strmi           r1, [r0], #4
+        strcs           r1, [r0], #4
+        strcs           r1, [r0], #4
+        lsls            r12, r12, #2
+        strcsh          r1, [r0], #2
+memset_route:
+        /*
+         * Decide where to route for the maximum copy sizes.  Note that we
+         * build q0 and q1 depending on if we'll need it, so that's
+         * interwoven here as well.
+         */
+        vdup.u32        d0, r1
+        cmp             r2, #16
+        blt             memset_8
+        vmov            d1, d0
+        cmp             r2, #64
+        blt             memset_16
+        vmov            q1, q0
+        cmp             r2, #128
+        blt             memset_32
+memset_128:
+        mov             r12, r2, lsr #7
+memset_128_loop:
+        vst1.64         {q0, q1}, [r0]!
+        vst1.64         {q0, q1}, [r0]!
+        vst1.64         {q0, q1}, [r0]!
+        vst1.64         {q0, q1}, [r0]!
+        subs            r12, r12, #1
+        bne             memset_128_loop
+        ands            r2, r2, #0x7f
+        beq             memset_end
+memset_32:
+        movs            r12, r2, lsr #5
+        beq             memset_16
+memset_32_loop:
+        subs            r12, r12, #1
+        vst1.64         {q0, q1}, [r0]!
+        bne             memset_32_loop
+        ands            r2, r2, #0x1f
+        beq             memset_end
+memset_16:
+        movs            r12, r2, lsr #4
+        beq             memset_8
+memset_16_loop:
+        subs            r12, r12, #1
+        vst1.32         {q0}, [r0]!
+        bne             memset_16_loop
+        ands            r2, r2, #0xf
+        beq             memset_end
+        /*
+         * memset_8 isn't a loop, since we try to do our loops at 16
+         * bytes and above.  We should loop there, then drop down here
+         * to finish the <16-byte versions.  Same for memset_4 and
+         * memset_1.
+         */
+memset_8:
+        cmp             r2, #8
+        blt             memset_4
+        subs            r2, r2, #8
+        vst1.32         {d0}, [r0]!
+memset_4:
+        cmp             r2, #4
+        blt             memset_2
+        subs            r2, r2, #4
+        str             r1, [r0], #4
+memset_2:
+        cmp             r2, #0
+        ble             memset_end
+        strh            r1, [r0], #2
+memset_end:
+        pop             {r0}
+        bx              lr
+
+        .endfunc
+        .end
diff --git a/legacy/src/opts/memset32_neon.S b/legacy/src/opts/memset32_neon.S
new file mode 100644
index 0000000..a9eaa0e
--- /dev/null
+++ b/legacy/src/opts/memset32_neon.S
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright (c) 2009,2010, Code Aurora Forum. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ ***************************************************************************/
+
+	.code 32
+	.fpu neon
+	.align 4
+	.globl	memset32_neon
+	.func
+
+	/* r0 = buffer, r1 = value, r2 = times to write */
+memset32_neon:
+	cmp		r2, #1
+	streq		r1, [r0], #4
+	bxeq		lr
+
+	cmp		r2, #4
+	bgt		memset32_neon_start
+	cmp		r2, #0
+	bxeq		lr
+memset32_neon_small:
+	str		r1, [r0], #4
+	subs		r2, r2, #1
+	bne		memset32_neon_small
+	bx		lr
+memset32_neon_start:
+	cmp		r2, #16
+	blt		memset32_dropthru
+	vdup.32		q0, r1
+	vmov		q1, q0
+	cmp		r2, #32
+	blt		memset32_16
+	cmp		r2, #64
+	blt		memset32_32
+	cmp		r2, #128
+	blt		memset32_64
+memset32_128:
+	movs		r12, r2, lsr #7
+memset32_loop128:
+	subs		r12, r12, #1
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	bne		memset32_loop128
+	ands		r2, r2, #0x7f
+	bxeq		lr
+memset32_64:
+	movs		r12, r2, lsr #6
+	beq		memset32_32
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	ands		r2, r2, #0x3f
+	bxeq		lr
+memset32_32:
+	movs		r12, r2, lsr #5
+	beq		memset32_16
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+	ands		r2, r2, #0x1f
+	bxeq		lr
+memset32_16:
+	movs		r12, r2, lsr #4
+	beq		memset32_dropthru
+	and		r2, r2, #0xf
+	vst1.64		{q0, q1}, [r0]!
+	vst1.64		{q0, q1}, [r0]!
+memset32_dropthru:
+	rsb		r2, r2, #15
+	add		pc, pc, r2, lsl #2
+	nop
+	str		r1, [r0, #56]
+	str		r1, [r0, #52]
+	str		r1, [r0, #48]
+	str		r1, [r0, #44]
+	str		r1, [r0, #40]
+	str		r1, [r0, #36]
+	str		r1, [r0, #32]
+	str		r1, [r0, #28]
+	str		r1, [r0, #24]
+	str		r1, [r0, #20]
+	str		r1, [r0, #16]
+	str		r1, [r0, #12]
+	str		r1, [r0, #8]
+	str		r1, [r0, #4]
+	str		r1, [r0, #0]
+	bx		lr
+
+	.endfunc
+	.end
diff --git a/legacy/src/opts/opts_check_SSE2.cpp b/legacy/src/opts/opts_check_SSE2.cpp
new file mode 100644
index 0000000..be1b4a1
--- /dev/null
+++ b/legacy/src/opts/opts_check_SSE2.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+#include "SkBitmapProcState_opts_SSE2.h"
+#include "SkBitmapProcState_opts_SSSE3.h"
+#include "SkBlitMask.h"
+#include "SkBlitRow_opts_SSE2.h"
+#include "SkUtils_opts_SSE2.h"
+#include "SkUtils.h"
+
+/* 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
+   in this directory should be compiled with -msse2. */
+
+
+#ifdef _MSC_VER
+static inline void getcpuid(int info_type, int info[4]) {
+    __asm {
+        mov    eax, [info_type]
+        cpuid
+        mov    edi, [info]
+        mov    [edi], eax
+        mov    [edi+4], ebx
+        mov    [edi+8], ecx
+        mov    [edi+12], edx
+    }
+}
+#else
+#if defined(__x86_64__)
+static inline void getcpuid(int info_type, int info[4]) {
+    asm volatile (
+        "cpuid \n\t"
+        : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3])
+        : "a"(info_type)
+    );
+}
+#else
+static inline void getcpuid(int info_type, int info[4]) {
+    // We save and restore ebx, so this code can be compatible with -fPIC
+    asm volatile (
+        "pushl %%ebx      \n\t"
+        "cpuid            \n\t"
+        "movl %%ebx, %1   \n\t"
+        "popl %%ebx       \n\t"
+        : "=a"(info[0]), "=r"(info[1]), "=c"(info[2]), "=d"(info[3])
+        : "a"(info_type)
+    );
+}
+#endif
+#endif
+
+#if defined(__x86_64__) || defined(_WIN64)
+/* All x86_64 machines have SSE2, so don't even bother checking. */
+static inline bool hasSSE2() {
+    return true;
+}
+#else
+
+static inline bool hasSSE2() {
+    int cpu_info[4] = { 0 };
+    getcpuid(1, cpu_info);
+    return (cpu_info[3] & (1<<26)) != 0;
+}
+#endif
+
+static inline bool hasSSSE3() {
+    int cpu_info[4] = { 0 };
+    getcpuid(1, cpu_info);
+    return (cpu_info[2] & 0x200) != 0;
+}
+
+static bool cachedHasSSE2() {
+    static bool gHasSSE2 = hasSSE2();
+    return gHasSSE2;
+}
+
+static bool cachedHasSSSE3() {
+    static bool gHasSSSE3 = hasSSSE3();
+    return gHasSSSE3;
+}
+
+void SkBitmapProcState::platformProcs() {
+    if (cachedHasSSSE3()) {
+        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;
+        }
+    } 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 (cachedHasSSSE3() || cachedHasSSE2()) {
+        if (fMatrixProc == ClampX_ClampY_filter_scale) {
+            fMatrixProc = ClampX_ClampY_filter_scale_SSE2;
+        } else if (fMatrixProc == ClampX_ClampY_nofilter_scale) {
+            fMatrixProc = ClampX_ClampY_nofilter_scale_SSE2;
+        }
+
+        if (fMatrixProc == ClampX_ClampY_filter_affine) {
+            fMatrixProc = ClampX_ClampY_filter_affine_SSE2;
+        } else if (fMatrixProc == ClampX_ClampY_nofilter_affine) {
+            fMatrixProc = ClampX_ClampY_nofilter_affine_SSE2;
+        }
+    }
+}
+
+static SkBlitRow::Proc32 platform_32_procs[] = {
+    NULL,                               // S32_Opaque,
+    S32_Blend_BlitRow32_SSE2,           // S32_Blend,
+    S32A_Opaque_BlitRow32_SSE2,         // S32A_Opaque
+    S32A_Blend_BlitRow32_SSE2,          // S32A_Blend,
+};
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs4444(unsigned flags) {
+    return NULL;
+}
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs565(unsigned flags) {
+    return NULL;
+}
+
+SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
+    if (cachedHasSSE2()) {
+        return Color32_SSE2;
+    } else {
+        return NULL;
+    }
+}
+
+SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
+    if (cachedHasSSE2()) {
+        return platform_32_procs[flags];
+    } else {
+        return NULL;
+    }
+}
+
+
+SkBlitMask::ColorProc SkBlitMask::PlatformColorProcs(SkBitmap::Config dstConfig,
+                                                     SkMask::Format maskFormat,
+                                                     SkColor color) {
+    if (SkMask::kA8_Format != maskFormat) {
+        return NULL;
+    }
+    
+    ColorProc proc = NULL;
+    if (cachedHasSSE2()) {
+        switch (dstConfig) {
+            case SkBitmap::kARGB_8888_Config:
+                // The SSE2 version is not (yet) faster for black, so we check
+                // for that.
+                if (SK_ColorBLACK != color) {
+                    proc = SkARGB32_A8_BlitMask_SSE2;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+    return proc;
+}
+
+SkBlitMask::BlitLCD16RowProc SkBlitMask::PlatformBlitRowProcs16(bool isOpaque) {
+    if (cachedHasSSE2()) {
+        if (isOpaque) {
+            return SkBlitLCD16OpaqueRow_SSE2;
+        } else {
+            return SkBlitLCD16Row_SSE2;
+        }
+    } else {
+        return NULL;
+    }
+
+}
+SkBlitMask::RowProc SkBlitMask::PlatformRowProcs(SkBitmap::Config dstConfig,
+                                                 SkMask::Format maskFormat,
+                                                 RowFlags flags) {
+    return NULL;
+}
+
+SkMemset16Proc SkMemset16GetPlatformProc() {
+    if (cachedHasSSE2()) {
+        return sk_memset16_SSE2;
+    } else {
+        return NULL;
+    }
+}
+
+SkMemset32Proc SkMemset32GetPlatformProc() {
+    if (cachedHasSSE2()) {
+        return sk_memset32_SSE2;
+    } else {
+        return NULL;
+    }
+}
diff --git a/legacy/src/opts/opts_check_arm.cpp b/legacy/src/opts/opts_check_arm.cpp
new file mode 100644
index 0000000..20ec8a1
--- /dev/null
+++ b/legacy/src/opts/opts_check_arm.cpp
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * Copyright 2006-2010, The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ ***************************************************************************/
+
+/* Changes:
+ * 2011-04-01 ARM
+ *    Merged the functions from src/opts/opts_check_arm_neon.cpp
+ *    Modified to return ARM version of memset16 and memset32 if no neon
+ *    available in the core
+ */
+
+#include "SkUtils.h"
+
+#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
+extern "C" void memset16_neon(uint16_t dst[], uint16_t value, int count);
+extern "C" void memset32_neon(uint32_t dst[], uint32_t value, int count);
+#endif
+
+#if defined(SK_CPU_LENDIAN)
+extern "C" void arm_memset16(uint16_t* dst, uint16_t value, int count);
+extern "C" void arm_memset32(uint32_t* dst, uint32_t value, int count);
+#endif
+
+SkMemset16Proc SkMemset16GetPlatformProc() {
+#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
+    return memset16_neon;
+#elif defined(SK_CPU_LENDIAN)
+    return arm_memset16;
+#else
+    return NULL;
+#endif
+}
+
+SkMemset32Proc SkMemset32GetPlatformProc() {
+#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
+    return memset32_neon;
+#elif defined(SK_CPU_LENDIAN)
+    return arm_memset32;
+#else
+    return NULL;
+#endif
+}
diff --git a/legacy/src/pipe/SkGPipePriv.h b/legacy/src/pipe/SkGPipePriv.h
new file mode 100644
index 0000000..2baf75a
--- /dev/null
+++ b/legacy/src/pipe/SkGPipePriv.h
@@ -0,0 +1,203 @@
+
+/*
+ * 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 SkGPipePriv_DEFINED
+#define SkGPipePriv_DEFINED
+
+#include "SkTypes.h"
+
+#define UNIMPLEMENTED
+
+// these must be contiguous, 0...N-1
+enum PaintFlats {
+    kColorFilter_PaintFlat,
+    kDrawLooper_PaintFlat,
+    kMaskFilter_PaintFlat,
+    kPathEffect_PaintFlat,
+    kRasterizer_PaintFlat,
+    kShader_PaintFlat,
+    kXfermode_PaintFlat,
+
+    kLast_PaintFlat = kXfermode_PaintFlat
+};
+#define kCount_PaintFlats   (kLast_PaintFlat + 1)
+
+enum DrawOps {
+    kSkip_DrawOp,   // skip an addition N bytes (N == data)
+
+    // these match Canvas apis
+    kClipPath_DrawOp,
+    kClipRegion_DrawOp,
+    kClipRect_DrawOp,
+    kConcat_DrawOp,
+    kDrawBitmap_DrawOp,
+    kDrawBitmapMatrix_DrawOp,
+    kDrawBitmapRect_DrawOp,
+    kDrawClear_DrawOp,
+    kDrawData_DrawOp,
+    kDrawPaint_DrawOp,
+    kDrawPath_DrawOp,
+    kDrawPicture_DrawOp,
+    kDrawPoints_DrawOp,
+    kDrawPosText_DrawOp,
+    kDrawPosTextH_DrawOp,
+    kDrawRect_DrawOp,
+    kDrawSprite_DrawOp,
+    kDrawText_DrawOp,
+    kDrawTextOnPath_DrawOp,
+    kDrawVertices_DrawOp,
+    kRestore_DrawOp,
+    kRotate_DrawOp,
+    kSave_DrawOp,
+    kSaveLayer_DrawOp,
+    kScale_DrawOp,
+    kSetMatrix_DrawOp,
+    kSkew_DrawOp,
+    kTranslate_DrawOp,
+
+    kPaintOp_DrawOp,
+
+    kDef_Typeface_DrawOp,
+    kDef_Flattenable_DrawOp,
+
+    // these are signals to playback, not drawing verbs
+    kDone_DrawOp,
+};
+
+/**
+ *  DrawOp packs into a 32bit int as follows
+ *
+ *  DrawOp:8 - Flags:4 - Data:20
+ *
+ *  Flags and Data are called out separately, so we can reuse Data between
+ *  different Ops that might have different Flags. e.g. Data might be a Paint
+ *  index for both drawRect (no flags) and saveLayer (does have flags).
+ *
+ *  All Ops that take a SkPaint use their Data field to store the index to
+ *  the paint (previously defined with kPaintOp_DrawOp).
+ */
+
+#define DRAWOPS_OP_BITS     8
+#define DRAWOPS_FLAG_BITS   4
+#define DRAWOPS_DATA_BITS   20
+
+#define DRAWOPS_OP_MASK     ((1 << DRAWOPS_OP_BITS) - 1)
+#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) {
+    return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS));
+}
+
+static unsigned DrawOp_unpackFlags(uint32_t op32) {
+    return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK;
+}
+
+static unsigned DrawOp_unpackData(uint32_t op32) {
+    return op32 & DRAWOPS_DATA_MASK;
+}
+
+static 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));
+
+    return (op << (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS)) |
+           (flags << DRAWOPS_DATA_BITS) |
+            data;
+}
+
+/** DrawOp specific flag bits
+ */
+
+enum {
+    kSaveLayer_HasBounds_DrawOpFlag = 1 << 0,
+    kSaveLayer_HasPaint_DrawOpFlag = 1 << 1,
+};
+enum {
+    kClear_HasColor_DrawOpFlag  = 1 << 0
+};
+enum {
+    kDrawTextOnPath_HasMatrix_DrawOpFlag = 1 << 0
+};
+enum {
+    kDrawVertices_HasTexs_DrawOpFlag     = 1 << 0,
+    kDrawVertices_HasColors_DrawOpFlag   = 1 << 1,
+    kDrawVertices_HasIndices_DrawOpFlag  = 1 << 2,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum PaintOps {
+    kReset_PaintOp,     // no arg
+    
+    kFlags_PaintOp,     // arg inline
+    kColor_PaintOp,     // arg 32
+    kStyle_PaintOp,     // arg inline
+    kJoin_PaintOp,      // arg inline
+    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
+    kTextSize_PaintOp,  // arg scalar - text
+    kTextScaleX_PaintOp,// arg scalar - text
+    kTextSkewX_PaintOp, // arg scalar - text
+    kTypeface_PaintOp,  // arg inline (index) - text
+
+    kFlatIndex_PaintOp, // flags=paintflat, data=index
+};
+
+#define PAINTOPS_OP_BITS     8
+#define PAINTOPS_FLAG_BITS   4
+#define PAINTOPS_DATA_BITS   20
+
+#define PAINTOPS_OP_MASK     ((1 << PAINTOPS_OP_BITS) - 1)
+#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) {
+    return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS));
+}
+
+static unsigned PaintOp_unpackFlags(uint32_t op32) {
+    return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK;
+}
+
+static unsigned PaintOp_unpackData(uint32_t op32) {
+    return op32 & PAINTOPS_DATA_MASK;
+}
+
+static 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) {
+    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) {
+    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;
+}
+
+#endif
diff --git a/legacy/src/pipe/SkGPipeRead.cpp b/legacy/src/pipe/SkGPipeRead.cpp
new file mode 100644
index 0000000..f30f105
--- /dev/null
+++ b/legacy/src/pipe/SkGPipeRead.cpp
@@ -0,0 +1,547 @@
+
+/*
+ * 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 "SkPaint.h"
+#include "SkGPipe.h"
+#include "SkGPipePriv.h"
+#include "SkReader32.h"
+#include "SkStream.h"
+
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+static void set_paintflat(SkPaint* paint, SkFlattenable* obj, unsigned paintFlat) {
+    SkASSERT(paintFlat < kCount_PaintFlats);
+    switch (paintFlat) {
+        case kColorFilter_PaintFlat:
+            paint->setColorFilter((SkColorFilter*)obj);
+            break;
+        case kDrawLooper_PaintFlat:
+            paint->setLooper((SkDrawLooper*)obj);
+            break;
+        case kMaskFilter_PaintFlat:
+            paint->setMaskFilter((SkMaskFilter*)obj);
+            break;
+        case kPathEffect_PaintFlat:
+            paint->setPathEffect((SkPathEffect*)obj);
+            break;
+        case kRasterizer_PaintFlat:
+            paint->setRasterizer((SkRasterizer*)obj);
+            break;
+        case kShader_PaintFlat:
+            paint->setShader((SkShader*)obj);
+            break;
+        case kXfermode_PaintFlat:
+            paint->setXfermode((SkXfermode*)obj);
+            break;
+        default:
+            SkDEBUGFAIL("never gets here");
+    }
+}
+
+template <typename T> class SkRefCntTDArray : public SkTDArray<T> {
+public:
+    ~SkRefCntTDArray() { this->unrefAll(); }
+};
+
+class SkGPipeState {
+public:
+    SkGPipeState();
+    ~SkGPipeState();
+
+    void setReader(SkFlattenableReadBuffer* reader) {
+        fReader = reader;
+        fReader->setFactoryArray(&fFactoryArray);
+    }
+
+    const SkPaint& paint() const { return fPaint; }
+    SkPaint* editPaint() { return &fPaint; }
+    
+    SkFlattenable* getFlat(unsigned index) const {
+        if (0 == index) {
+            return NULL;
+        }
+        return fFlatArray[index - 1];
+    }
+
+    void defFlattenable(PaintFlats pf, int index) {
+        SkASSERT(index == fFlatArray.count() + 1);
+        SkFlattenable* obj = fReader->readFlattenable();
+        *fFlatArray.append() = obj;
+    }
+
+    void addTypeface() {
+        size_t size = fReader->readU32();
+        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:
+    SkPaint                   fPaint;
+    SkTDArray<SkFlattenable*> fFlatArray;
+    SkTDArray<SkTypeface*>    fTypefaces;
+    SkTDArray<SkFlattenable::Factory> fFactoryArray;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> const T* skip(SkReader32* reader, int count = 1) {
+    SkASSERT(count >= 0);
+    size_t size = sizeof(T) * count;
+    SkASSERT(SkAlign4(size) == size);
+    return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+template <typename T> const T* skipAlign(SkReader32* reader, int count = 1) {
+    SkASSERT(count >= 0);
+    size_t size = SkAlign4(sizeof(T) * count);
+    return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+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));
+}
+
+static void clipRegion_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    SkRegion rgn;
+    SkReadRegion(reader, &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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    SkReadMatrix(reader, &matrix);
+    canvas->setMatrix(matrix);
+}
+
+static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    SkReadMatrix(reader, &matrix);
+    canvas->concat(matrix);
+}
+
+static void scale_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->scale(param[0], param[1]);
+}
+
+static void skew_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->skew(param[0], param[1]);
+}
+
+static void rotate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    canvas->rotate(reader->readScalar());
+}
+
+static void translate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->translate(param[0], param[1]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void save_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                    SkGPipeState* state) {
+    canvas->save((SkCanvas::SaveFlags)DrawOp_unpackData(op32));
+}
+
+static void saveLayer_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    unsigned flags = DrawOp_unpackFlags(op32);
+    SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags)DrawOp_unpackData(op32);
+
+    const SkRect* bounds = NULL;
+    if (flags & kSaveLayer_HasBounds_DrawOpFlag) {
+        bounds = skip<SkRect>(reader);
+    }
+    const SkPaint* paint = NULL;
+    if (flags & kSaveLayer_HasPaint_DrawOpFlag) {
+        paint = &state->paint();
+    }
+    canvas->saveLayer(bounds, paint, saveFlags);
+}
+
+static void restore_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                       SkGPipeState* state) {
+    canvas->restore();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawClear_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    SkColor color = 0;
+    if (DrawOp_unpackFlags(op32) & kClear_HasColor_DrawOpFlag) {
+        color = reader->readU32();
+    }
+    canvas->clear(color);
+}
+
+static void drawPaint_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    canvas->drawPaint(state->paint());
+}
+
+static void drawPoints_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    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());
+}
+
+static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    canvas->drawRect(*skip<SkRect>(reader), 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());
+}
+
+static void drawVertices_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                            SkGPipeState* state) {
+    unsigned flags = DrawOp_unpackFlags(op32);
+
+    SkCanvas::VertexMode mode = (SkCanvas::VertexMode)reader->readU32();
+    int vertexCount = reader->readU32();
+    const SkPoint* verts = skip<SkPoint>(reader, vertexCount);
+
+    const SkPoint* texs = NULL;
+    if (flags & kDrawVertices_HasTexs_DrawOpFlag) {
+        texs = skip<SkPoint>(reader, vertexCount);
+    }
+
+    const SkColor* colors = NULL;
+    if (flags & kDrawVertices_HasColors_DrawOpFlag) {
+        colors = skip<SkColor>(reader, vertexCount);
+    }
+
+    // TODO: flatten/unflatten xfermodes
+    SkXfermode* xfer = NULL;
+
+    int indexCount = 0;
+    const uint16_t* indices = NULL;
+    if (flags & kDrawVertices_HasIndices_DrawOpFlag) {
+        indexCount = reader->readU32();
+        indices = skipAlign<uint16_t>(reader, indexCount);
+    }
+
+    canvas->drawVertices(mode, vertexCount, verts, texs, colors, xfer,
+                         indices, indexCount, state->paint());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    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());
+}
+
+static void drawPosText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    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());
+}
+
+static void drawPosTextH_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+    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());
+}
+
+static void drawTextOnPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                              SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+
+    SkPath path;
+    path.unflatten(*reader);
+
+    SkMatrix matrixStorage;
+    const SkMatrix* matrix = NULL;
+    if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) {
+        SkReadMatrix(reader, &matrixStorage);
+        matrix = &matrixStorage;
+    }
+
+    canvas->drawTextOnPath(text, len, path, matrix, state->paint());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawBitmap_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawBitmapMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                                SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                              SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawData_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    // since we don't have a paint, we can use data for our (small) sizes
+    size_t size = DrawOp_unpackData(op32);
+    if (0 == size) {
+        size = reader->readU32();
+    }
+    const void* data = reader->skip(SkAlign4(size));
+    canvas->drawData(data, size);
+}
+
+static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                           SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void paintOp_rp(SkCanvas*, SkReader32* reader, uint32_t op32,
+                       SkGPipeState* state) {
+    size_t offset = reader->offset();
+    size_t stop = offset + PaintOp_unpackData(op32);
+    SkPaint* p = state->editPaint();
+
+    do {
+        uint32_t p32 = reader->readU32();
+        unsigned op = PaintOp_unpackOp(p32);
+        unsigned data = PaintOp_unpackData(p32);
+
+//        SkDebugf(" read %08X op=%d flags=%d data=%d\n", p32, op, done, data);
+
+        switch (op) {
+            case kReset_PaintOp: p->reset(); break;
+            case kFlags_PaintOp: p->setFlags(data); break;
+            case kColor_PaintOp: p->setColor(reader->readU32()); break;
+            case kStyle_PaintOp: p->setStyle((SkPaint::Style)data); break;
+            case kJoin_PaintOp: p->setStrokeJoin((SkPaint::Join)data); break;
+            case kCap_PaintOp: p->setStrokeCap((SkPaint::Cap)data); break;
+            case kWidth_PaintOp: p->setStrokeWidth(reader->readScalar()); break;
+            case kMiter_PaintOp: p->setStrokeMiter(reader->readScalar()); break;
+            case kEncoding_PaintOp:
+                p->setTextEncoding((SkPaint::TextEncoding)data);
+                break;
+            case kHinting_PaintOp: p->setHinting((SkPaint::Hinting)data); break;
+            case kAlign_PaintOp: p->setTextAlign((SkPaint::Align)data); break;
+            case kTextSize_PaintOp: p->setTextSize(reader->readScalar()); break;
+            case kTextScaleX_PaintOp: p->setTextScaleX(reader->readScalar()); break;
+            case kTextSkewX_PaintOp: p->setTextSkewX(reader->readScalar()); break;
+
+            case kFlatIndex_PaintOp: {
+                PaintFlats pf = (PaintFlats)PaintOp_unpackFlags(p32);
+                unsigned index = data;
+                set_paintflat(p, state->getFlat(index), pf);
+                break;
+            }
+
+            case kTypeface_PaintOp: state->setTypeface(p, data); break;
+            default: SkDEBUGFAIL("bad paintop"); return;
+        }
+        SkASSERT(reader->offset() <= stop);
+    } while (reader->offset() < stop);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void def_Typeface_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState* state) {
+    state->addTypeface();
+}
+
+static void def_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32,
+                             SkGPipeState* state) {
+    PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32);
+    unsigned index = DrawOp_unpackData(op32);
+    state->defFlattenable(pf, index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) {
+    size_t bytes = DrawOp_unpackData(op32);
+    (void)reader->skip(bytes);
+}
+
+static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
+
+typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
+
+static const ReadProc gReadTable[] = {
+    skip_rp,
+    clipPath_rp,
+    clipRegion_rp,
+    clipRect_rp,
+    concat_rp,
+    drawBitmap_rp,
+    drawBitmapMatrix_rp,
+    drawBitmapRect_rp,
+    drawClear_rp,
+    drawData_rp,
+    drawPaint_rp,
+    drawPath_rp,
+    drawPicture_rp,
+    drawPoints_rp,
+    drawPosText_rp,
+    drawPosTextH_rp,
+    drawRect_rp,
+    drawSprite_rp,
+    drawText_rp,
+    drawTextOnPath_rp,
+    drawVertices_rp,
+    restore_rp,
+    rotate_rp,
+    save_rp,
+    saveLayer_rp,
+    scale_rp,
+    setMatrix_rp,
+    skew_rp,
+    translate_rp,
+
+    paintOp_rp,
+    def_Typeface_rp,
+    def_PaintFlat_rp,
+
+    done_rp
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGPipeState::SkGPipeState() {}
+
+SkGPipeState::~SkGPipeState() {
+    fTypefaces.safeUnrefAll();
+    fFlatArray.safeUnrefAll();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+SkGPipeReader::SkGPipeReader(SkCanvas* target) {
+    SkSafeRef(target);
+    fCanvas = target;
+    fState = NULL;
+}
+
+SkGPipeReader::~SkGPipeReader() {
+    SkSafeUnref(fCanvas);
+    delete fState;
+}
+
+SkGPipeReader::Status SkGPipeReader::playback(const void* data, size_t length,
+                                              size_t* bytesRead, bool readAtom) {
+    if (NULL == fCanvas) {
+        return kError_Status;
+    }
+
+    if (NULL == fState) {
+        fState = new SkGPipeState;
+    }
+
+    SkASSERT(SK_ARRAY_COUNT(gReadTable) == (kDone_DrawOp + 1));
+
+    const ReadProc* table = gReadTable;
+    SkFlattenableReadBuffer reader(data, length);
+    SkCanvas* canvas = fCanvas;
+    Status status = kEOF_Status;
+
+    fState->setReader(&reader);
+    while (!reader.eof()) {
+        uint32_t op32 = reader.readU32();
+        unsigned op = DrawOp_unpackOp(op32);
+        SkDEBUGCODE(DrawOps drawOp = (DrawOps)op;)
+        
+        if (op >= SK_ARRAY_COUNT(gReadTable)) {
+            SkDebugf("---- bad op during GPipeState::playback\n");
+            status = kError_Status;
+            break;
+        }
+        if (kDone_DrawOp == op) {
+            status = kDone_Status;
+            break;
+        }
+        table[op](canvas, &reader, op32, fState);
+        if (readAtom && 
+            (table[op] != paintOp_rp &&
+             table[op] != def_Typeface_rp &&
+             table[op] != def_PaintFlat_rp
+             )) {
+                status = kReadAtom_Status;
+                break;
+            }
+    }
+
+    if (bytesRead) {
+        *bytesRead = reader.offset();
+    }
+    return status;
+}
+
+
diff --git a/legacy/src/pipe/SkGPipeWrite.cpp b/legacy/src/pipe/SkGPipeWrite.cpp
new file mode 100644
index 0000000..ee6e2c7
--- /dev/null
+++ b/legacy/src/pipe/SkGPipeWrite.cpp
@@ -0,0 +1,785 @@
+
+/*
+ * 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 "SkData.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkGPipe.h"
+#include "SkGPipePriv.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"
+
+static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) {
+    SkASSERT(paintFlat < kCount_PaintFlats);
+    switch (paintFlat) {
+        case kColorFilter_PaintFlat:    return paint.getColorFilter();
+        case kDrawLooper_PaintFlat:     return paint.getLooper();
+        case kMaskFilter_PaintFlat:     return paint.getMaskFilter();
+        case kPathEffect_PaintFlat:     return paint.getPathEffect();
+        case kRasterizer_PaintFlat:     return paint.getRasterizer();
+        case kShader_PaintFlat:         return paint.getShader();
+        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;
+    typeface->serialize(&stream);
+    size_t size = stream.getOffset();
+    if (writer) {
+        writer->write32(size);
+        SkAutoDataUnref data(stream.copyToData());
+        writer->write(data.data(), size);
+    }
+    return 4 + size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeCanvas : public SkCanvas {
+public:
+    SkGPipeCanvas(SkGPipeController*, SkWriter32*, SkFactorySet*);
+    virtual ~SkGPipeCanvas();
+
+    void finish() {
+        if (!fDone) {
+            if (this->needOpBytes()) {
+                this->writeOp(kDone_DrawOp);
+                this->doNotify();
+            }
+            fDone = true;
+        }
+    }
+
+    // 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 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&);
+    virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                            const SkPaint*);
+    virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
+                                const SkRect& dst, const SkPaint*);
+    virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                  const SkPaint*);
+    virtual void drawSprite(const SkBitmap&, int left, int top,
+                            const SkPaint*);
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, const SkPaint&);
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint&);
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                      const SkScalar xpos[], SkScalar constY, const SkPaint&);
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                            const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint&);
+    virtual void drawPicture(SkPicture& picture);
+    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);
+
+private:
+    SkFactorySet* fFactorySet;  // optional, only used if cross-process
+    SkGPipeController* fController;
+    SkWriter32& fWriter;
+    size_t      fBlockSize; // amount allocated for writer
+    size_t      fBytesNotified;
+    bool        fDone;
+
+    SkRefCntSet fTypefaceSet;
+
+    uint32_t getTypefaceID(SkTypeface*);
+
+    inline void writeOp(DrawOps op, unsigned flags, unsigned data) {
+        fWriter.write32(DrawOp_packOpFlagData(op, flags, data));
+    }
+
+    inline void writeOp(DrawOps op) {
+        fWriter.write32(DrawOp_packOpFlagData(op, 0, 0));
+    }
+
+    bool needOpBytes(size_t size = 0);
+
+    inline void doNotify() {
+        if (!fDone) {
+            size_t bytes = fWriter.size() - fBytesNotified;
+            fController->notifyWritten(bytes);
+            fBytesNotified += bytes;
+        }
+    }
+
+    struct FlatData {
+        uint32_t    fIndex; // always > 0
+        uint32_t    fSize;
+
+        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;
+    int fCurrFlatIndex[kCount_PaintFlats];
+    int flattenToIndex(SkFlattenable* obj, PaintFlats);
+
+    SkPaint fPaint;
+    void writePaint(const SkPaint&);
+
+    class AutoPipeNotify {
+    public:
+        AutoPipeNotify(SkGPipeCanvas* canvas) : fCanvas(canvas) {}
+        ~AutoPipeNotify() { fCanvas->doNotify(); }
+    private:
+        SkGPipeCanvas* fCanvas;
+    };
+    friend class AutoPipeNotify;
+
+    typedef SkCanvas INHERITED;
+};
+
+// return 0 for NULL (or unflattenable obj), or index-base-1
+int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
+    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);
+        }
+    }
+    return fFlatArray[index]->fIndex;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define MIN_BLOCK_SIZE  (16 * 1024)
+
+SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
+                             SkWriter32* writer, SkFactorySet* fset)
+        : fWriter(*writer) {
+    fFactorySet = fset;
+    fController = controller;
+    fDone = false;
+    fBlockSize = 0; // need first block from controller
+    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);
+    SkDevice* device = SkNEW_ARGS(SkDevice, (bitmap));
+    this->setDevice(device)->unref();
+}
+
+SkGPipeCanvas::~SkGPipeCanvas() {
+    this->finish();
+
+    fFlatArray.freeAll();
+}
+
+bool SkGPipeCanvas::needOpBytes(size_t needed) {
+    if (fDone) {
+        return false;
+    }
+
+    needed += 4;  // size of DrawOp atom
+    if (fWriter.size() + needed > fBlockSize) {
+        void* block = fController->requestBlock(MIN_BLOCK_SIZE, &fBlockSize);
+        if (NULL == block) {
+            fDone = true;
+            return false;
+        }
+        fWriter.reset(block, fBlockSize);
+        fBytesNotified = 0;
+    }
+    return true;
+}
+
+uint32_t SkGPipeCanvas::getTypefaceID(SkTypeface* face) {
+    uint32_t id = 0; // 0 means default/null typeface
+    if (face) {
+        id = fTypefaceSet.find(face);
+        if (0 == id) {
+            id = fTypefaceSet.add(face);
+            size_t size = writeTypeface(NULL, face);
+            if (this->needOpBytes(size)) {
+                this->writeOp(kDef_Typeface_DrawOp);
+                writeTypeface(&fWriter, face);
+            }
+        }
+    }
+    return id;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define NOTIFY_SETUP(canvas)    \
+    AutoPipeNotify apn(canvas)
+
+int SkGPipeCanvas::save(SaveFlags flags) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes()) {
+        this->writeOp(kSave_DrawOp, 0, flags);
+    }
+    return this->INHERITED::save(flags);
+}
+
+int SkGPipeCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                             SaveFlags saveFlags) {
+    NOTIFY_SETUP(this);
+    size_t size = 0;
+    unsigned opFlags = 0;
+
+    if (bounds) {
+        opFlags |= kSaveLayer_HasBounds_DrawOpFlag;
+        size += sizeof(SkRect);
+    }
+    if (paint) {
+        opFlags |= kSaveLayer_HasPaint_DrawOpFlag;
+        this->writePaint(*paint);
+    }
+
+    if (this->needOpBytes(size)) {
+        this->writeOp(kSaveLayer_DrawOp, opFlags, saveFlags);
+        if (bounds) {
+            fWriter.writeRect(*bounds);
+        }
+    }
+
+    // we just pass on the save, so we don't create a layer
+    return this->INHERITED::save(saveFlags);
+}
+
+void SkGPipeCanvas::restore() {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes()) {
+        this->writeOp(kRestore_DrawOp);
+    }
+    this->INHERITED::restore();
+}
+
+bool SkGPipeCanvas::translate(SkScalar dx, SkScalar dy) {
+    if (dx || dy) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(2 * sizeof(SkScalar))) {
+            this->writeOp(kTranslate_DrawOp);
+            fWriter.writeScalar(dx);
+            fWriter.writeScalar(dy);
+        }
+    }
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkGPipeCanvas::scale(SkScalar sx, SkScalar sy) {
+    if (sx || sy) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(2 * sizeof(SkScalar))) {
+            this->writeOp(kScale_DrawOp);
+            fWriter.writeScalar(sx);
+            fWriter.writeScalar(sy);
+        }
+    }
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkGPipeCanvas::rotate(SkScalar degrees) {
+    if (degrees) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(sizeof(SkScalar))) {
+            this->writeOp(kRotate_DrawOp);
+            fWriter.writeScalar(degrees);
+        }
+    }
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkGPipeCanvas::skew(SkScalar sx, SkScalar sy) {
+    if (sx || sy) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(2 * sizeof(SkScalar))) {
+            this->writeOp(kSkew_DrawOp);
+            fWriter.writeScalar(sx);
+            fWriter.writeScalar(sy);
+        }
+    }
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkGPipeCanvas::concat(const SkMatrix& matrix) {
+    if (!matrix.isIdentity()) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(matrix.flatten(NULL))) {
+            this->writeOp(kConcat_DrawOp);
+            SkWriteMatrix(&fWriter, matrix);
+        }
+    }
+    return this->INHERITED::concat(matrix);
+}
+
+void SkGPipeCanvas::setMatrix(const SkMatrix& matrix) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(matrix.flatten(NULL))) {
+        this->writeOp(kSetMatrix_DrawOp);
+        SkWriteMatrix(&fWriter, matrix);
+    }
+    this->INHERITED::setMatrix(matrix);
+}
+
+bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(sizeof(SkRect))) {
+        this->writeOp(kClipRect_DrawOp, 0, rgnOp);
+        fWriter.writeRect(rect);
+    }
+    return this->INHERITED::clipRect(rect, rgnOp);
+}
+
+bool SkGPipeCanvas::clipPath(const SkPath& path, SkRegion::Op rgnOp) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(estimateFlattenSize(path))) {
+        this->writeOp(kClipPath_DrawOp, 0, rgnOp);
+        path.flatten(fWriter);
+    }
+    // we just pass on the bounds of the path
+    return this->INHERITED::clipRect(path.getBounds(), rgnOp);
+}
+
+bool SkGPipeCanvas::clipRegion(const SkRegion& region, SkRegion::Op rgnOp) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(region.flatten(NULL))) {
+        this->writeOp(kClipRegion_DrawOp, 0, rgnOp);
+        SkWriteRegion(&fWriter, region);
+    }
+    return this->INHERITED::clipRegion(region, rgnOp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGPipeCanvas::clear(SkColor color) {
+    NOTIFY_SETUP(this);
+    unsigned flags = 0;
+    if (color) {
+        flags |= kClear_HasColor_DrawOpFlag;
+    }
+    if (this->needOpBytes(sizeof(SkColor))) {
+        this->writeOp(kDrawClear_DrawOp, flags, 0);
+        if (color) {
+            fWriter.write32(color);
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPaint(const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes()) {
+        this->writeOp(kDrawPaint_DrawOp);
+    }
+}
+
+void SkGPipeCanvas::drawPoints(PointMode mode, size_t count,
+                                   const SkPoint pts[], const SkPaint& paint) {
+    if (count) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        if (this->needOpBytes(4 + count * sizeof(SkPoint))) {
+            this->writeOp(kDrawPoints_DrawOp, mode, 0);
+            fWriter.write32(count);
+            fWriter.write(pts, count * sizeof(SkPoint));
+        }
+    }
+}
+
+void SkGPipeCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes(sizeof(SkRect))) {
+        this->writeOp(kDrawRect_DrawOp);
+        fWriter.writeRect(rect);
+    }
+}
+
+void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes(estimateFlattenSize(path))) {
+        this->writeOp(kDrawPath_DrawOp);
+        path.flatten(fWriter);
+    }
+}
+
+void SkGPipeCanvas::drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                                   const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapRect(const SkBitmap&, const SkIRect* src,
+                                       const SkRect& dst, const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                         const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawSprite(const SkBitmap&, int left, int top,
+                                   const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+                                 SkScalar y, const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        if (this->needOpBytes(4 + SkAlign4(byteLength) + 2 * sizeof(SkScalar))) {
+            this->writeOp(kDrawText_DrawOp);
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+            fWriter.writeScalar(x);
+            fWriter.writeScalar(y);
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPosText(const void* text, size_t byteLength,
+                                const SkPoint pos[], const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        int count = paint.textToGlyphs(text, byteLength, NULL);
+        if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkPoint))) {
+            this->writeOp(kDrawPosText_DrawOp);
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+            fWriter.write32(count);
+            fWriter.write(pos, count * sizeof(SkPoint));
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                 const SkScalar xpos[], SkScalar constY,
+                                 const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        int count = paint.textToGlyphs(text, byteLength, NULL);
+        if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkScalar) + 4)) {
+            this->writeOp(kDrawPosTextH_DrawOp);
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+            fWriter.write32(count);
+            fWriter.write(xpos, count * sizeof(SkScalar));
+            fWriter.writeScalar(constY);
+        }
+    }
+}
+
+void SkGPipeCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                                   const SkPath& path, const SkMatrix* matrix,
+                                   const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        unsigned flags = 0;
+        size_t size = 4 + SkAlign4(byteLength) + estimateFlattenSize(path);
+        if (matrix) {
+            flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag;
+            size += matrix->flatten(NULL);
+        }
+        this->writePaint(paint);
+        if (this->needOpBytes(size)) {
+            this->writeOp(kDrawTextOnPath_DrawOp, flags, 0);
+
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+
+            path.flatten(fWriter);
+            if (matrix) {
+                SkWriteMatrix(&fWriter, *matrix);
+            }
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPicture(SkPicture& picture) {
+    // we want to playback the picture into individual draw calls
+    this->INHERITED::drawPicture(picture);
+}
+
+void SkGPipeCanvas::drawVertices(VertexMode mode, int vertexCount,
+                                 const SkPoint vertices[], const SkPoint texs[],
+                                 const SkColor colors[], SkXfermode*,
+                                 const uint16_t indices[], int indexCount,
+                                 const SkPaint& paint) {
+    if (0 == vertexCount) {
+        return;
+    }
+
+    NOTIFY_SETUP(this);
+    size_t size = 4 + vertexCount * sizeof(SkPoint);
+    this->writePaint(paint);
+    unsigned flags = 0;
+    if (texs) {
+        flags |= kDrawVertices_HasTexs_DrawOpFlag;
+        size += vertexCount * sizeof(SkPoint);
+    }
+    if (colors) {
+        flags |= kDrawVertices_HasColors_DrawOpFlag;
+        size += vertexCount * sizeof(SkColor);
+    }
+    if (indices && indexCount > 0) {
+        flags |= kDrawVertices_HasIndices_DrawOpFlag;
+        size += 4 + SkAlign4(indexCount * sizeof(uint16_t));
+    }
+
+    if (this->needOpBytes(size)) {
+        this->writeOp(kDrawVertices_DrawOp, flags, 0);
+        fWriter.write32(mode);
+        fWriter.write32(vertexCount);
+        fWriter.write(vertices, vertexCount * sizeof(SkPoint));
+        if (texs) {
+            fWriter.write(texs, vertexCount * sizeof(SkPoint));
+        }
+        if (colors) {
+            fWriter.write(colors, vertexCount * sizeof(SkColor));
+        }
+
+        // TODO: flatten xfermode
+
+        if (indices && indexCount > 0) {
+            fWriter.write32(indexCount);
+            fWriter.writePad(indices, indexCount * sizeof(uint16_t));
+        }
+    }
+}
+
+void SkGPipeCanvas::drawData(const void* ptr, size_t size) {
+    if (size && ptr) {
+        NOTIFY_SETUP(this);
+        unsigned data = 0;
+        if (size < (1 << DRAWOPS_DATA_BITS)) {
+            data = (unsigned)size;
+        }
+        if (this->needOpBytes(4 + SkAlign4(size))) {
+            this->writeOp(kDrawData_DrawOp, 0, data);
+            if (0 == data) {
+                fWriter.write32(size);
+            }
+            fWriter.writePad(ptr, size);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> uint32_t castToU32(T value) {
+    union {
+        T           fSrc;
+        uint32_t    fDst;
+    } data;
+    data.fSrc = value;
+    return data.fDst;
+}
+
+void SkGPipeCanvas::writePaint(const SkPaint& paint) {
+    SkPaint& base = fPaint;
+    uint32_t storage[32];
+    uint32_t* ptr = storage;
+
+    if (base.getFlags() != paint.getFlags()) {
+        *ptr++ = PaintOp_packOpData(kFlags_PaintOp, paint.getFlags());
+        base.setFlags(paint.getFlags());
+    }
+    if (base.getColor() != paint.getColor()) {
+        *ptr++ = PaintOp_packOp(kColor_PaintOp);
+        *ptr++ = paint.getColor();
+        base.setColor(paint.getColor());
+    }
+    if (base.getStyle() != paint.getStyle()) {
+        *ptr++ = PaintOp_packOpData(kStyle_PaintOp, paint.getStyle());
+        base.setStyle(paint.getStyle());
+    }
+    if (base.getStrokeJoin() != paint.getStrokeJoin()) {
+        *ptr++ = PaintOp_packOpData(kJoin_PaintOp, paint.getStrokeJoin());
+        base.setStrokeJoin(paint.getStrokeJoin());
+    }
+    if (base.getStrokeCap() != paint.getStrokeCap()) {
+        *ptr++ = PaintOp_packOpData(kCap_PaintOp, paint.getStrokeCap());
+        base.setStrokeCap(paint.getStrokeCap());
+    }
+    if (base.getStrokeWidth() != paint.getStrokeWidth()) {
+        *ptr++ = PaintOp_packOp(kWidth_PaintOp);
+        *ptr++ = castToU32(paint.getStrokeWidth());
+        base.setStrokeWidth(paint.getStrokeWidth());
+    }
+    if (base.getStrokeMiter() != paint.getStrokeMiter()) {
+        *ptr++ = PaintOp_packOp(kMiter_PaintOp);
+        *ptr++ = castToU32(paint.getStrokeMiter());
+        base.setStrokeMiter(paint.getStrokeMiter());
+    }
+    if (base.getTextEncoding() != paint.getTextEncoding()) {
+        *ptr++ = PaintOp_packOpData(kEncoding_PaintOp, paint.getTextEncoding());
+        base.setTextEncoding(paint.getTextEncoding());
+    }
+    if (base.getHinting() != paint.getHinting()) {
+        *ptr++ = PaintOp_packOpData(kHinting_PaintOp, paint.getHinting());
+        base.setHinting(paint.getHinting());
+    }
+    if (base.getTextAlign() != paint.getTextAlign()) {
+        *ptr++ = PaintOp_packOpData(kAlign_PaintOp, paint.getTextAlign());
+        base.setTextAlign(paint.getTextAlign());
+    }
+    if (base.getTextSize() != paint.getTextSize()) {
+        *ptr++ = PaintOp_packOp(kTextSize_PaintOp);
+        *ptr++ = castToU32(paint.getTextSize());
+        base.setTextSize(paint.getTextSize());
+    }
+    if (base.getTextScaleX() != paint.getTextScaleX()) {
+        *ptr++ = PaintOp_packOp(kTextScaleX_PaintOp);
+        *ptr++ = castToU32(paint.getTextScaleX());
+        base.setTextScaleX(paint.getTextScaleX());
+    }
+    if (base.getTextSkewX() != paint.getTextSkewX()) {
+        *ptr++ = PaintOp_packOp(kTextSkewX_PaintOp);
+        *ptr++ = castToU32(paint.getTextSkewX());
+        base.setTextSkewX(paint.getTextSkewX());
+    }
+
+    if (!SkTypeface::Equal(base.getTypeface(), paint.getTypeface())) {
+        uint32_t id = this->getTypefaceID(paint.getTypeface());
+        *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id);
+        base.setTypeface(paint.getTypeface());
+    }
+
+    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]) {
+            *ptr++ = PaintOp_packOpFlagData(kFlatIndex_PaintOp, i, index);
+            fCurrFlatIndex[i] = index;
+        }
+    }
+
+    size_t size = (char*)ptr - (char*)storage;
+    if (size && this->needOpBytes(size)) {
+        this->writeOp(kPaintOp_DrawOp, 0, size);
+        fWriter.write(storage, size);
+        for (size_t i = 0; i < size/4; i++) {
+//            SkDebugf("[%d] %08X\n", i, storage[i]);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+SkGPipeWriter::SkGPipeWriter() : fWriter(0) {
+    fCanvas = NULL;
+}
+
+SkGPipeWriter::~SkGPipeWriter() {
+    this->endRecording();
+    SkSafeUnref(fCanvas);
+}
+
+SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller,
+                                        uint32_t flags) {
+    if (NULL == fCanvas) {
+        fWriter.reset(NULL, 0);
+        fFactorySet.reset();
+        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter,
+                                             (flags & kCrossProcess_Flag) ?
+                                             &fFactorySet : NULL));
+    }
+    return fCanvas;
+}
+
+void SkGPipeWriter::endRecording() {
+    if (fCanvas) {
+        fCanvas->finish();
+        fCanvas->unref();
+        fCanvas = NULL;
+    }
+}
+
diff --git a/legacy/src/ports/FontHostConfiguration_android.cpp b/legacy/src/ports/FontHostConfiguration_android.cpp
new file mode 100644
index 0000000..420ad1c
--- /dev/null
+++ b/legacy/src/ports/FontHostConfiguration_android.cpp
@@ -0,0 +1,245 @@
+/* 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.
+*/
+
+#include "FontHostConfiguration_android.h"
+#include "SkLanguage.h"
+#include "SkTDArray.h"
+#include "SkTypeface.h"
+#include <expat.h>
+#if !defined(SK_BUILD_FOR_ANDROID_NDK)
+    #include <cutils/properties.h>
+#endif
+
+#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
+#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
+#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
+
+// These defines are used to determine the kind of tag that we're currently
+// populating with data. We only care about the sibling tags nameset and fileset
+// for now.
+#define NO_TAG 0
+#define NAMESET_TAG 1
+#define FILESET_TAG 2
+
+/**
+ * The FamilyData structure is passed around by the parser so that each handler
+ * can read these variables that are relevant to the current parsing.
+ */
+struct FamilyData {
+    FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
+            parser(parserRef), families(familiesRef), currentTag(NO_TAG) {};
+
+    XML_Parser *parser;                // The expat parser doing the work
+    SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
+    FontFamily *currentFamily;         // The current family being created
+    FontFileInfo *currentFontInfo;     // The current fontInfo being created
+    int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
+};
+
+/**
+ * Handler for arbitrary text. This is used to parse the text inside each name
+ * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
+ */
+void textHandler(void *data, const char *s, int len) {
+    FamilyData *familyData = (FamilyData*) data;
+    // Make sure we're in the right state to store this name information
+    if (familyData->currentFamily &&
+            (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) {
+        // Malloc new buffer to store the string
+        char *buff;
+        buff = (char*) malloc((len + 1) * sizeof(char));
+        strncpy(buff, s, len);
+        buff[len] = '\0';
+        switch (familyData->currentTag) {
+        case NAMESET_TAG:
+            *(familyData->currentFamily->fNames.append()) = buff;
+            break;
+        case FILESET_TAG:
+            if (familyData->currentFontInfo) {
+                familyData->currentFontInfo->fFileName = buff;
+            }
+            break;
+        default:
+            // Noop - don't care about any text that's not in the Fonts or Names list
+            break;
+        }
+    }
+}
+
+/**
+ * Handler for font files. This processes the attributes for language and
+ * variants then lets textHandler handle the actual file name
+ */
+void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
+    FontFileInfo* newFileInfo = new FontFileInfo();
+    if (attributes) {
+        int currentAttributeIndex = 0;
+        while (attributes[currentAttributeIndex]) {
+            const char* attributeName = attributes[currentAttributeIndex];
+            const char* attributeValue = attributes[currentAttributeIndex+1];
+            int nameLength = strlen(attributeName);
+            int valueLength = strlen(attributeValue);
+            if (strncmp(attributeName, "variant", nameLength) == 0) {
+                if (strncmp(attributeValue, "elegant", valueLength) == 0) {
+                    newFileInfo->fVariant = SkPaint::kElegant_Variant;
+                } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
+                    newFileInfo->fVariant = SkPaint::kCompact_Variant;
+                }
+            } else if (strncmp(attributeName, "lang", nameLength) == 0) {
+                newFileInfo->fLanguage = SkLanguage(attributeValue);
+            }
+            //each element is a pair of attributeName/attributeValue string pairs
+            currentAttributeIndex += 2;
+        }
+    }
+    *(familyData->currentFamily->fFontFileArray.append()) = newFileInfo;
+    familyData->currentFontInfo = newFileInfo;
+    XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+}
+
+/**
+ * Handler for the start of a tag. The only tags we expect are family, nameset,
+ * fileset, name, and file.
+ */
+void startElementHandler(void *data, const char *tag, const char **atts) {
+    FamilyData *familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (strncmp(tag, "family", len)== 0) {
+        familyData->currentFamily = new FontFamily();
+        familyData->currentFamily->order = -1;
+        // The Family tag has an optional "order" attribute with an integer value >= 0
+        // If this attribute does not exist, the default value is -1
+        for (int i = 0; atts[i] != NULL; i += 2) {
+            const char* attribute = atts[i];
+            const char* valueString = atts[i+1];
+            int value;
+            int len = sscanf(valueString, "%d", &value);
+            if (len > 0) {
+                familyData->currentFamily->order = value;
+            }
+        }
+    } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
+        familyData->currentTag = NAMESET_TAG;
+    } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
+        familyData->currentTag = FILESET_TAG;
+    } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
+        // If it's a Name, parse the text inside
+        XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+    } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
+        // If it's a file, parse the attributes, then parse the text inside
+        fontFileElementHandler(familyData, atts);
+    }
+}
+
+/**
+ * Handler for the end of tags. We only care about family, nameset, fileset,
+ * name, and file.
+ */
+void endElementHandler(void *data, const char *tag) {
+    FamilyData *familyData = (FamilyData*) data;
+    int len = strlen(tag);
+    if (strncmp(tag, "family", len)== 0) {
+        // Done parsing a Family - store the created currentFamily in the families array
+        *familyData->families.append() = familyData->currentFamily;
+        familyData->currentFamily = NULL;
+    } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
+        familyData->currentTag = NO_TAG;
+    } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
+        familyData->currentTag = NO_TAG;
+    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
+            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
+        // Disable the arbitrary text handler installed to load Name data
+        XML_SetCharacterDataHandler(*familyData->parser, NULL);
+    }
+}
+
+/**
+ * This function parses the given filename and stores the results in the given
+ * families array.
+ */
+void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
+    XML_Parser parser = XML_ParserCreate(NULL);
+    FamilyData *familyData = new FamilyData(&parser, families);
+    XML_SetUserData(parser, familyData);
+    XML_SetElementHandler(parser, startElementHandler, endElementHandler);
+    FILE *file = fopen(filename, "r");
+    // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
+    // are optional - failure here is okay because one of these optional files may not exist.
+    if (file == NULL) {
+        return;
+    }
+    char buffer[512];
+    bool done = false;
+    while (!done) {
+        fgets(buffer, sizeof(buffer), file);
+        int len = strlen(buffer);
+        if (feof(file) != 0) {
+            done = true;
+        }
+        XML_Parse(parser, buffer, len, done);
+    }
+    fclose(file);
+}
+
+void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
+    parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies);
+}
+
+void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) {
+    SkTDArray<FontFamily*> vendorFonts;
+    parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts);
+    parseConfigFile(VENDOR_FONTS_FILE, vendorFonts);
+
+    // This loop inserts the vendor fallback fonts in the correct order in the
+    // overall fallbacks list.
+    int currentOrder = -1;
+    for (int i = 0; i < vendorFonts.count(); ++i) {
+        FontFamily* family = vendorFonts[i];
+        int order = family->order;
+        if (order < 0) {
+            if (currentOrder < 0) {
+                // Default case - just add it to the end of the fallback list
+                *fallbackFonts.append() = family;
+            } else {
+                // no order specified on this font, but we're incrementing the order
+                // based on an earlier order insertion request
+                *fallbackFonts.insert(currentOrder++) = family;
+            }
+        } else {
+            // Add the font into the fallback list in the specified order. Set
+            // currentOrder for correct placement of other fonts in the vendor list.
+            *fallbackFonts.insert(order) = family;
+            currentOrder = order + 1;
+        }
+    }
+}
+
+/**
+ * Loads data on font families from various expected configuration files. The
+ * resulting data is returned in the given fontFamilies array.
+ */
+void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
+    SkTDArray<FontFamily*> fallbackFonts;
+
+    getSystemFontFamilies(fontFamilies);
+    getFallbackFontFamilies(fallbackFonts);
+
+    // Append all fallback fonts to system fonts
+    for (int i = 0; i < fallbackFonts.count(); ++i) {
+        *fontFamilies.append() = fallbackFonts[i];
+    }
+}
diff --git a/legacy/src/ports/FontHostConfiguration_android.h b/legacy/src/ports/FontHostConfiguration_android.h
new file mode 100644
index 0000000..6734b08
--- /dev/null
+++ b/legacy/src/ports/FontHostConfiguration_android.h
@@ -0,0 +1,68 @@
+/* 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.
+*/
+#ifndef FONTHOSTCONFIGURATION_ANDROID_H_
+#define FONTHOSTCONFIGURATION_ANDROID_H_
+
+#include "SkTypes.h"
+
+#include "SkLanguage.h"
+#include "SkPaint.h"
+#include "SkTDArray.h"
+
+struct FontFileInfo {
+    FontFileInfo() : fFileName(NULL), fVariant(SkPaint::kDefault_Variant),
+            fLanguage() {
+    }
+
+    const char*          fFileName;
+    SkPaint::FontVariant fVariant;
+    SkLanguage           fLanguage;
+};
+
+/**
+ * The FontFamily data structure is created during parsing and handed back to
+ * Skia to fold into its representation of font families. fNames is the list of
+ * font names that alias to a font family. fontFileArray is the list of information
+ * about each file.  Order is the priority order for the font. This is
+ * used internally to determine the order in which to place fallback fonts as
+ * they are read from the configuration files.
+ */
+struct FontFamily {
+    SkTDArray<const char*>   fNames;
+    SkTDArray<FontFileInfo*> fFontFileArray;
+    int order;
+};
+
+/**
+ * Parses all system font configuration files and returns the results in an
+ * array of FontFamily structures.
+ */
+void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies);
+
+/**
+ * Parse only the core system font configuration file and return the results in
+ * an array of FontFamily structures.
+ */
+void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies);
+
+/**
+ * Parse the fallback and vendor system font configuration files and return the
+ * results in an array of FontFamily structures.
+ */
+void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts);
+
+#endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */
diff --git a/legacy/src/ports/SkDebug_android.cpp b/legacy/src/ports/SkDebug_android.cpp
new file mode 100644
index 0000000..8e7d1d4
--- /dev/null
+++ b/legacy/src/ports/SkDebug_android.cpp
@@ -0,0 +1,22 @@
+
+/*
+ * 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 "SkTypes.h"
+
+static const size_t kBufferSize = 256;
+
+#define LOG_TAG "skia"
+#include <android/log.h>
+
+void SkDebugf(const char format[], ...) {
+    va_list args;
+    va_start(args, format);
+    __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, format, args);
+    va_end(args);
+}
diff --git a/legacy/src/ports/SkDebug_brew.cpp b/legacy/src/ports/SkDebug_brew.cpp
new file mode 100644
index 0000000..b7ad3ef
--- /dev/null
+++ b/legacy/src/ports/SkDebug_brew.cpp
@@ -0,0 +1,28 @@
+/* libs/corecg/SkDebug_brew.cpp
+ *
+ * Copyright 2009, The Android Open Source Project
+ * Copyright 2009, Company 100, Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_BREW
+
+static const size_t kBufferSize = 256;
+
+#include <AEEStdLib.h>
+#include <stdarg.h>
+
+void SkDebugf(const char format[], ...) {
+    char    buffer[kBufferSize + 1];
+    va_list args;
+    va_start(args, format);
+    VSNPRINTF(buffer, kBufferSize, format, args);
+    va_end(args);
+    DBGPRINTF(buffer);
+}
+
+#endif SK_BUILD_FOR_BREW
diff --git a/legacy/src/ports/SkDebug_stdio.cpp b/legacy/src/ports/SkDebug_stdio.cpp
new file mode 100644
index 0000000..bc3d98b
--- /dev/null
+++ b/legacy/src/ports/SkDebug_stdio.cpp
@@ -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.
+ */
+
+
+#include "SkTypes.h"
+
+static const size_t kBufferSize = 2048;
+
+#include <stdarg.h>
+#include <stdio.h>
+
+void SkDebugf(const char format[], ...) {
+    char    buffer[kBufferSize + 1];
+    va_list args;
+    va_start(args, format);
+    vsnprintf(buffer, kBufferSize, format, args);
+    va_end(args);
+    fprintf(stderr, "%s", buffer);
+}
+
diff --git a/legacy/src/ports/SkDebug_win.cpp b/legacy/src/ports/SkDebug_win.cpp
new file mode 100644
index 0000000..b3077f1
--- /dev/null
+++ b/legacy/src/ports/SkDebug_win.cpp
@@ -0,0 +1,29 @@
+
+/*
+ * 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 "SkTypes.h"
+
+static const size_t kBufferSize = 2048;
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <Windows.h>
+
+void SkDebugf(const char format[], ...) {
+    char    buffer[kBufferSize + 1];
+    va_list args;
+    va_start(args, format);
+    vsnprintf(buffer, kBufferSize, format, args);
+    va_end(args);
+
+    OutputDebugStringA(buffer);
+    printf(buffer);
+}
+
diff --git a/legacy/src/ports/SkFontHost_FONTPATH.cpp b/legacy/src/ports/SkFontHost_FONTPATH.cpp
new file mode 100644
index 0000000..bd8f102
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_FONTPATH.cpp
@@ -0,0 +1,322 @@
+
+/*
+ * 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 "SkDescriptor.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include <stdio.h>
+
+/* define this if we can use mmap() to access fonts from the filesystem */
+#define SK_CAN_USE_MMAP 
+
+#ifndef SK_FONTPATH
+    #define SK_FONTPATH "the complete path for a font file"
+#endif
+
+struct FontFaceRec {
+    const char* fFileName;    
+    uint8_t     fFamilyIndex;
+    SkBool8     fBold;
+    SkBool8     fItalic;
+
+    static const FontFaceRec& FindFace(const FontFaceRec rec[], int count,
+                                       int isBold, int isItalic);
+};
+
+struct FontFamilyRec {
+    const FontFaceRec*  fFaces;
+    int                 fFaceCount;
+};
+
+const FontFaceRec& FontFaceRec::FindFace(const FontFaceRec rec[], int count,
+                                         int isBold, int isItalic)
+{
+    SkASSERT(count > 0);
+    
+    int i;
+
+    // look for an exact match
+    for (i = 0; i < count; i++) {
+        if (rec[i].fBold == isBold && rec[i].fItalic == isItalic)
+            return rec[i];
+    }
+    // look for a match in the bold field
+    for (i = 0; i < count; i++) {
+        if (rec[i].fBold == isBold)
+            return rec[i];
+    }
+    // look for a normal/regular face
+    for (i = 0; i < count; i++) {
+        if (!rec[i].fBold && !rec[i].fItalic)
+            return rec[i];
+    }
+    // give up
+    return rec[0];
+}
+
+enum {
+    DEFAULT_FAMILY_INDEX,
+    
+    FAMILY_INDEX_COUNT
+};
+
+static const FontFaceRec gDefaultFaces[] = {
+    { SK_FONTPATH, DEFAULT_FAMILY_INDEX, 0,  0 }
+};
+
+// This table must be in the same order as the ..._FAMILY_INDEX enum specifies
+static const FontFamilyRec gFamilies[] = {
+    { gDefaultFaces,   SK_ARRAY_COUNT(gDefaultFaces)  }
+};
+
+#define DEFAULT_FAMILY_INDEX            DEFAULT_FAMILY_INDEX
+#define DEFAULT_FAMILY_FACE_INDEX       0
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* map common "web" font names to our font list */
+
+struct FontFamilyMatchRec {
+    const char* fLCName;
+    int         fFamilyIndex;
+};
+
+/*  This is a table of synonyms for collapsing font names
+    down to their pseudo-equivalents (i.e. in terms of fonts
+    we actually have.)
+    Keep this sorted by the first field so we can do a binary search.
+    If this gets big, we could switch to a hash...
+*/
+static const FontFamilyMatchRec gMatches[] = {
+#if 0
+    { "Ahem",               Ahem_FAMILY_INDEX },
+    { "arial",              SANS_FAMILY_INDEX },
+    { "courier",            MONO_FAMILY_INDEX },
+    { "courier new",        MONO_FAMILY_INDEX },
+    { "cursive",            SERIF_FAMILY_INDEX },
+    { "fantasy",            SERIF_FAMILY_INDEX },
+    { "georgia",            SERIF_FAMILY_INDEX },
+    { "goudy",              SERIF_FAMILY_INDEX },
+    { "helvetica",          SANS_FAMILY_INDEX },
+    { "palatino",           SERIF_FAMILY_INDEX },
+    { "tahoma",             SANS_FAMILY_INDEX },
+    { "sans-serif",         SANS_FAMILY_INDEX },
+    { "serif",              SERIF_FAMILY_INDEX },
+    { "times",              SERIF_FAMILY_INDEX },
+    { "times new roman",    SERIF_FAMILY_INDEX },
+    { "verdana",            SANS_FAMILY_INDEX }
+#endif
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTSearch.h"
+
+static bool contains_only_ascii(const char s[])
+{
+    for (;;)
+    {
+        int c = *s++;
+        if (c == 0)
+            break;
+        if ((c >> 7) != 0)
+            return false;
+    }
+    return true;
+}
+
+#define TRACE_FONT_NAME(code)
+//#define TRACE_FONT_NAME(code)   code
+
+const FontFamilyRec* find_family_rec(const char target[])
+{
+    int     index;
+
+    //  If we're asked for a font name that contains non-ascii,
+    //  1) SkStrLCSearch can't handle it
+    //  2) All of our fonts are have ascii names, so...
+
+TRACE_FONT_NAME(printf("----------------- font request <%s>", target);)
+
+    if (contains_only_ascii(target))
+    {
+        // Search for the font by matching the entire name
+        index = SkStrLCSearch(&gMatches[0].fLCName, SK_ARRAY_COUNT(gMatches),
+                              target, sizeof(gMatches[0]));
+        if (index >= 0)
+        {
+            TRACE_FONT_NAME(printf(" found %d\n", index);)
+            return &gFamilies[gMatches[index].fFamilyIndex];
+        }
+    }
+
+    // Sniff for key words...
+
+#if 0
+    if (strstr(target, "sans") || strstr(target, "Sans"))
+    {
+        TRACE_FONT_NAME(printf(" found sans\n");)
+        return &gFamilies[SANS_FAMILY_INDEX];
+    }
+    if (strstr(target, "serif") || strstr(target, "Serif"))
+    {
+        TRACE_FONT_NAME(printf(" found serif\n");)
+        return &gFamilies[SERIF_FAMILY_INDEX];
+    }
+    if (strstr(target, "mono") || strstr(target, "Mono"))
+    {
+        TRACE_FONT_NAME(printf(" found mono\n");)
+        return &gFamilies[MONO_FAMILY_INDEX];
+    }
+#endif
+
+    TRACE_FONT_NAME(printf(" use default\n");)
+    // we give up, just give them the default font
+    return &gFamilies[DEFAULT_FAMILY_INDEX];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const FontFaceRec* get_default_face()
+{
+    return &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX];
+}
+
+static SkTypeface::Style get_style(const FontFaceRec& face) {
+    int style = 0;
+    if (face.fBold) {
+        style |= SkTypeface::kBold;
+    }
+    if (face.fItalic) {
+        style |= SkTypeface::kItalic;
+    }
+    return static_cast<SkTypeface::Style>(style);
+}
+
+// This global const reference completely identifies the face
+static uint32_t get_id(const FontFaceRec& face) {
+    uintptr_t id = reinterpret_cast<uintptr_t>(&face);
+    return static_cast<uint32_t>(id);
+}
+
+class FontFaceRec_Typeface : public SkTypeface {
+public:
+    FontFaceRec_Typeface(const FontFaceRec& face) :
+                         SkTypeface(get_style(face), get_id(face)),
+                         fFace(face)
+    {
+    }
+
+    // This global const reference completely identifies the face
+    const FontFaceRec& fFace;
+};
+
+static const FontFaceRec* get_typeface_rec(const SkTypeface* face)
+{
+    const FontFaceRec_Typeface* f = (FontFaceRec_Typeface*)face;
+    return f ? &f->fFace : get_default_face();
+}
+
+static uint32_t ptr2uint32(const void* p)
+{
+    // cast so we avoid warnings on 64bit machines that a ptr difference
+    // which might be 64bits is being trucated from 64 to 32
+    return (uint32_t)((char*)p - (char*)0);
+}
+
+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];
+    else if (familyName)
+        family = find_family_rec(familyName);
+    else
+        family = &gFamilies[DEFAULT_FAMILY_INDEX];
+
+    const FontFaceRec& face = FontFaceRec::FindFace(family->fFaces,
+                                            family->fFaceCount,
+                                            (style & SkTypeface::kBold) != 0,
+                                            (style & SkTypeface::kItalic) != 0);
+
+    // if we're returning our input parameter, no need to create a new instance
+    if (familyFace != NULL &&
+            &((FontFaceRec_Typeface*)familyFace)->fFace == &face)
+    {
+        familyFace->ref();
+        return (SkTypeface*)familyFace;
+    }
+    return SkNEW_ARGS(FontFaceRec_Typeface, (face));
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
+    sk_throw();  // not implemented
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    sk_throw();  // not implemented
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    sk_throw();  // not implemented
+    return NULL;
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t fontID) {
+    sk_throw();  // not implemented
+    return NULL;
+}
+
+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* tface, SkWStream* stream) {
+    const FontFaceRec* face = &((const FontFaceRec_Typeface*)tface)->fFace;
+    stream->write(face, sizeof(face));
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    const FontFaceRec* face;
+    stream->read(&face, sizeof(face));
+    return new FontFaceRec_Typeface(*face);
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(
+                                                const SkScalerContext::Rec& rec)
+{
+    const FontFaceRec* face = get_default_face();
+
+    SkAutoDescriptor    ad(sizeof(rec) + sizeof(face) +
+                           SkDescriptor::ComputeOverhead(2));
+    SkDescriptor*       desc = ad.getDesc();
+    SkScalerContext::Rec* newRec;
+
+    desc->init();
+    newRec = reinterpret_cast<SkScalerContext::Rec*>(
+        desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec));
+    newRec->fFontID = get_id(*face);
+    desc->computeChecksum();
+
+    return SkFontHost::CreateScalerContext(desc);
+}
+
diff --git a/legacy/src/ports/SkFontHost_FreeType.cpp b/legacy/src/ports/SkFontHost_FreeType.cpp
new file mode 100644
index 0000000..6c03bf9
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_FreeType.cpp
@@ -0,0 +1,1781 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDescriptor.h"
+#include "SkFDot6.h"
+#include "SkFontHost.h"
+#include "SkMask.h"
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkScalerContext.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_SIZES_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_TYPE1_TABLES_H
+#include FT_BITMAP_H
+// In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
+#include FT_SYNTHESIS_H
+#include FT_XFREE86_H
+#ifdef FT_LCD_FILTER_H
+#include FT_LCD_FILTER_H
+#endif
+
+#ifdef   FT_ADVANCES_H
+#include FT_ADVANCES_H
+#endif
+
+#if 0
+// Also include the files by name for build tools which require this.
+#include <freetype/freetype.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftsizes.h>
+#include <freetype/tttables.h>
+#include <freetype/ftadvanc.h>
+#include <freetype/ftlcdfil.h>
+#include <freetype/ftbitmap.h>
+#include <freetype/ftsynth.h>
+#endif
+
+//#define ENABLE_GLYPH_SPEW     // for tracing calls
+//#define DUMP_STRIKE_CREATION
+
+//#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) {
+    switch (rec.fMaskFormat) {
+        case SkMask::kLCD16_Format:
+        case SkMask::kLCD32_Format:
+            return true;
+        default:
+            return false;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+struct SkFaceRec;
+
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
+static int          gFTCount;
+static FT_Library   gFTLibrary;
+static SkFaceRec*   gFaceRecHead;
+static bool         gLCDSupportValid;  // true iff |gLCDSupport| has been set.
+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;
+
+// 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() {
+    FT_Error err = FT_Init_FreeType(&gFTLibrary);
+    if (err) {
+        return false;
+    }
+
+    // Setup LCD filtering. This reduces colour fringes for LCD rendered
+    // 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.
+    }
+#else
+    gLCDSupport = false;
+#endif
+    gLCDSupportValid = true;
+
+    return true;
+}
+
+class SkScalerContext_FreeType : public SkScalerContext {
+public:
+    SkScalerContext_FreeType(const SkDescriptor* desc);
+    virtual ~SkScalerContext_FreeType();
+
+    bool success() const {
+        return fFaceRec != NULL &&
+               fFTSize != NULL &&
+               fFace != NULL;
+    }
+
+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 SkUnichar generateGlyphToChar(uint16_t glyph);
+
+private:
+    SkFaceRec*  fFaceRec;
+    FT_Face     fFace;              // reference to shared face in gFaceRecHead
+    FT_Size     fFTSize;            // our own copy
+    SkFixed     fScaleX, fScaleY;
+    FT_Matrix   fMatrix22;
+    uint32_t    fLoadGlyphFlags;
+    bool        fDoLinearMetrics;
+    bool        fUseVertMetrics;
+
+    FT_Error setupSize();
+    void emboldenOutline(FT_Outline* outline);
+    void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox,
+                                bool snapToPixelBoundary = false);
+    void updateGlyphIfLCD(SkGlyph* glyph);
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+struct SkFaceRec {
+    SkFaceRec*      fNext;
+    FT_Face         fFace;
+    FT_StreamRec    fFTStream;
+    SkStream*       fSkStream;
+    uint32_t        fRefCnt;
+    uint32_t        fFontID;
+
+    // assumes ownership of the stream, will call unref() when its done
+    SkFaceRec(SkStream* strm, uint32_t fontID);
+    ~SkFaceRec() {
+        fSkStream->unref();
+    }
+};
+
+extern "C" {
+    static unsigned long sk_stream_read(FT_Stream       stream,
+                                        unsigned long   offset,
+                                        unsigned char*  buffer,
+                                        unsigned long   count ) {
+        SkStream* str = (SkStream*)stream->descriptor.pointer;
+
+        if (count) {
+            if (!str->rewind()) {
+                return 0;
+            } else {
+                unsigned long ret;
+                if (offset) {
+                    ret = str->read(NULL, offset);
+                    if (ret != offset) {
+                        return 0;
+                    }
+                }
+                ret = str->read(buffer, count);
+                if (ret != count) {
+                    return 0;
+                }
+                count = ret;
+            }
+        }
+        return count;
+    }
+
+    static void sk_stream_close( FT_Stream stream) {}
+}
+
+SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
+        : fSkStream(strm), fFontID(fontID) {
+//    SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm));
+
+    sk_bzero(&fFTStream, sizeof(fFTStream));
+    fFTStream.size = fSkStream->getLength();
+    fFTStream.descriptor.pointer = fSkStream;
+    fFTStream.read  = sk_stream_read;
+    fFTStream.close = sk_stream_close;
+}
+
+// Will return 0 on failure
+static SkFaceRec* ref_ft_face(uint32_t fontID) {
+    SkFaceRec* rec = gFaceRecHead;
+    while (rec) {
+        if (rec->fFontID == fontID) {
+            SkASSERT(rec->fFace);
+            rec->fRefCnt += 1;
+            return rec;
+        }
+        rec = rec->fNext;
+    }
+
+    SkStream* strm = SkFontHost::OpenStream(fontID);
+    if (NULL == strm) {
+        SkDEBUGF(("SkFontHost::OpenStream failed opening %x\n", fontID));
+        return 0;
+    }
+
+    // this passes ownership of strm to the rec
+    rec = SkNEW_ARGS(SkFaceRec, (strm, fontID));
+
+    FT_Open_Args    args;
+    memset(&args, 0, sizeof(args));
+    const void* memoryBase = strm->getMemoryBase();
+
+    if (NULL != memoryBase) {
+//printf("mmap(%s)\n", keyString.c_str());
+        args.flags = FT_OPEN_MEMORY;
+        args.memory_base = (const FT_Byte*)memoryBase;
+        args.memory_size = strm->getLength();
+    } else {
+//printf("fopen(%s)\n", keyString.c_str());
+        args.flags = FT_OPEN_STREAM;
+        args.stream = &rec->fFTStream;
+    }
+
+    int face_index;
+    int length = SkFontHost::GetFileName(fontID, NULL, 0, &face_index);
+    FT_Error err = FT_Open_Face(gFTLibrary, &args, length ? face_index : 0,
+                                &rec->fFace);
+
+    if (err) {    // bad filename, try the default font
+        fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID);
+        SkDELETE(rec);
+        return 0;
+    } else {
+        SkASSERT(rec->fFace);
+        //fprintf(stderr, "Opened font '%s'\n", filename.c_str());
+        rec->fNext = gFaceRecHead;
+        gFaceRecHead = rec;
+        rec->fRefCnt = 1;
+        return rec;
+    }
+}
+
+static void unref_ft_face(FT_Face face) {
+    SkFaceRec*  rec = gFaceRecHead;
+    SkFaceRec*  prev = NULL;
+    while (rec) {
+        SkFaceRec* next = rec->fNext;
+        if (rec->fFace == face) {
+            if (--rec->fRefCnt == 0) {
+                if (prev) {
+                    prev->fNext = next;
+                } else {
+                    gFaceRecHead = next;
+                }
+                FT_Done_Face(face);
+                SkDELETE(rec);
+            }
+            return;
+        }
+        prev = rec;
+        rec = next;
+    }
+    SkDEBUGFAIL("shouldn't get here, face not in list");
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+// Work around for old versions of freetype.
+static FT_Error getAdvances(FT_Face face, FT_UInt start, FT_UInt count,
+                           FT_Int32 loadFlags, FT_Fixed* advances) {
+#ifdef FT_ADVANCES_H
+    return FT_Get_Advances(face, start, count, loadFlags, advances);
+#else
+    if (!face || start >= face->num_glyphs ||
+            start + count > face->num_glyphs || loadFlags != FT_LOAD_NO_SCALE) {
+        return 6;  // "Invalid argument."
+    }
+    if (count == 0)
+        return 0;
+
+    for (int i = 0; i < count; i++) {
+        FT_Error err = FT_Load_Glyph(face, start + i, FT_LOAD_NO_SCALE);
+        if (err)
+            return err;
+        advances[i] = face->glyph->advance.x;
+    }
+
+    return 0;
+#endif
+}
+
+static bool canEmbed(FT_Face face) {
+#ifdef FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING
+    FT_UShort fsType = FT_Get_FSType_Flags(face);
+    return (fsType & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING |
+                      FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0;
+#else
+    // No embedding is 0x2 and bitmap embedding only is 0x200.
+    TT_OS2* os2_table;
+    if ((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
+        return (os2_table->fsType & 0x202) == 0;
+    }
+    return false;  // We tried, fail safe.
+#endif
+}
+
+static bool GetLetterCBox(FT_Face face, char letter, FT_BBox* bbox) {
+    const FT_UInt glyph_id = FT_Get_Char_Index(face, letter);
+    if (!glyph_id)
+        return false;
+    FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE);
+    FT_Outline_Get_CBox(&face->glyph->outline, bbox);
+    return true;
+}
+
+static bool getWidthAdvance(FT_Face face, int gId, int16_t* data) {
+    FT_Fixed advance = 0;
+    if (getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance)) {
+        return false;
+    }
+    SkASSERT(data);
+    *data = advance;
+    return true;
+}
+
+static void populate_glyph_to_unicode(FT_Face& face,
+                                      SkTDArray<SkUnichar>* glyphToUnicode) {
+    // Check and see if we have Unicode cmaps.
+    for (int i = 0; i < face->num_charmaps; ++i) {
+        // CMaps known to support Unicode:
+        // Platform ID   Encoding ID   Name
+        // -----------   -----------   -----------------------------------
+        // 0             0,1           Apple Unicode
+        // 0             3             Apple Unicode 2.0 (preferred)
+        // 3             1             Microsoft Unicode UCS-2
+        // 3             10            Microsoft Unicode UCS-4 (preferred)
+        //
+        // See Apple TrueType Reference Manual
+        // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html
+        // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html#ID
+        // Microsoft OpenType Specification
+        // http://www.microsoft.com/typography/otspec/cmap.htm
+
+        FT_UShort platformId = face->charmaps[i]->platform_id;
+        FT_UShort encodingId = face->charmaps[i]->encoding_id;
+
+        if (platformId != 0 && platformId != 3) {
+            continue;
+        }
+        if (platformId == 3 && encodingId != 1 && encodingId != 10) {
+            continue;
+        }
+        bool preferredMap = ((platformId == 3 && encodingId == 10) ||
+                             (platformId == 0 && encodingId == 3));
+
+        FT_Set_Charmap(face, face->charmaps[i]);
+        if (glyphToUnicode->isEmpty()) {
+            glyphToUnicode->setCount(face->num_glyphs);
+            memset(glyphToUnicode->begin(), 0,
+                   sizeof(SkUnichar) * face->num_glyphs);
+        }
+
+        // Iterate through each cmap entry.
+        FT_UInt glyphIndex;
+        for (SkUnichar charCode = FT_Get_First_Char(face, &glyphIndex);
+             glyphIndex != 0;
+             charCode = FT_Get_Next_Char(face, charCode, &glyphIndex)) {
+            if (charCode &&
+                    ((*glyphToUnicode)[glyphIndex] == 0 || preferredMap)) {
+                (*glyphToUnicode)[glyphIndex] = charCode;
+            }
+        }
+    }
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+#if defined(SK_BUILD_FOR_MAC)
+    return NULL;
+#else
+    SkAutoMutexAcquire ac(gFTMutex);
+    FT_Library libInit = NULL;
+    if (gFTCount == 0) {
+        if (!InitFreetype())
+            sk_throw();
+        libInit = gFTLibrary;
+    }
+    SkAutoTCallIProc<struct FT_LibraryRec_, FT_Done_FreeType> ftLib(libInit);
+    SkFaceRec* rec = ref_ft_face(fontID);
+    if (NULL == rec)
+        return NULL;
+    FT_Face face = rec->fFace;
+
+    SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+    info->fFontName.set(FT_Get_Postscript_Name(face));
+    info->fMultiMaster = FT_HAS_MULTIPLE_MASTERS(face);
+    info->fLastGlyphID = face->num_glyphs - 1;
+    info->fEmSize = 1000;
+
+    bool cid = false;
+    const char* fontType = FT_Get_X11_Font_Format(face);
+    if (strcmp(fontType, "Type 1") == 0) {
+        info->fType = SkAdvancedTypefaceMetrics::kType1_Font;
+    } else if (strcmp(fontType, "CID Type 1") == 0) {
+        info->fType = SkAdvancedTypefaceMetrics::kType1CID_Font;
+        cid = true;
+    } else if (strcmp(fontType, "CFF") == 0) {
+        info->fType = SkAdvancedTypefaceMetrics::kCFF_Font;
+    } else if (strcmp(fontType, "TrueType") == 0) {
+        info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+        cid = true;
+        TT_Header* ttHeader;
+        if ((ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face,
+                                                      ft_sfnt_head)) != NULL) {
+            info->fEmSize = ttHeader->Units_Per_EM;
+        }
+    }
+
+    info->fStyle = 0;
+    if (FT_IS_FIXED_WIDTH(face))
+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+    if (face->style_flags & FT_STYLE_FLAG_ITALIC)
+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+    // We should set either Symbolic or Nonsymbolic; Nonsymbolic if the font's
+    // character set is a subset of 'Adobe standard Latin.'
+    info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+
+    PS_FontInfoRec ps_info;
+    TT_Postscript* tt_info;
+    if (FT_Get_PS_Font_Info(face, &ps_info) == 0) {
+        info->fItalicAngle = ps_info.italic_angle;
+    } else if ((tt_info =
+                (TT_Postscript*)FT_Get_Sfnt_Table(face,
+                                                  ft_sfnt_post)) != NULL) {
+        info->fItalicAngle = SkFixedToScalar(tt_info->italicAngle);
+    } else {
+        info->fItalicAngle = 0;
+    }
+
+    info->fAscent = face->ascender;
+    info->fDescent = face->descender;
+
+    // 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++) {
+        FT_BBox bbox;
+        if (GetLetterCBox(face, stem_chars[i], &bbox)) {
+            int16_t width = bbox.xMax - bbox.xMin;
+            if (width > 0 && width < min_width) {
+                min_width = width;
+                info->fStemV = min_width;
+            }
+        }
+    }
+
+    TT_PCLT* pclt_info;
+    TT_OS2* os2_table;
+    if ((pclt_info = (TT_PCLT*)FT_Get_Sfnt_Table(face, ft_sfnt_pclt)) != NULL) {
+        info->fCapHeight = pclt_info->CapHeight;
+        uint8_t serif_style = pclt_info->SerifStyle & 0x3F;
+        if (serif_style >= 2 && serif_style <= 6)
+            info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+        else if (serif_style >= 9 && serif_style <= 12)
+            info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+    } else if ((os2_table =
+                (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
+        info->fCapHeight = os2_table->sCapHeight;
+    } else {
+        // Figure out a good guess for CapHeight: average the height of M and X.
+        FT_BBox m_bbox, x_bbox;
+        bool got_m, got_x;
+        got_m = GetLetterCBox(face, 'M', &m_bbox);
+        got_x = GetLetterCBox(face, 'X', &x_bbox);
+        if (got_m && got_x) {
+            info->fCapHeight = (m_bbox.yMax - m_bbox.yMin + x_bbox.yMax -
+                    x_bbox.yMin) / 2;
+        } else if (got_m && !got_x) {
+            info->fCapHeight = m_bbox.yMax - m_bbox.yMin;
+        } else if (!got_m && got_x) {
+            info->fCapHeight = x_bbox.yMax - x_bbox.yMin;
+        }
+    }
+
+    info->fBBox = SkIRect::MakeLTRB(face->bbox.xMin, face->bbox.yMax,
+                                    face->bbox.xMax, face->bbox.yMin);
+
+    if (!canEmbed(face) || !FT_IS_SCALABLE(face) ||
+            info->fType == SkAdvancedTypefaceMetrics::kOther_Font) {
+        perGlyphInfo = SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo;
+    }
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+        if (FT_IS_FIXED_WIDTH(face)) {
+            appendRange(&info->fGlyphWidths, 0);
+            int16_t advance = face->max_advance_width;
+            info->fGlyphWidths->fAdvance.append(1, &advance);
+            finishRange(info->fGlyphWidths.get(), 0,
+                        SkAdvancedTypefaceMetrics::WidthRange::kDefault);
+        } else if (!cid) {
+            appendRange(&info->fGlyphWidths, 0);
+            // So as to not blow out the stack, get advances in batches.
+            for (int gID = 0; gID < face->num_glyphs; gID += 128) {
+                FT_Fixed advances[128];
+                int advanceCount = 128;
+                if (gID + advanceCount > face->num_glyphs)
+                    advanceCount = face->num_glyphs - gID + 1;
+                getAdvances(face, gID, advanceCount, FT_LOAD_NO_SCALE,
+                            advances);
+                for (int i = 0; i < advanceCount; i++) {
+                    int16_t advance = advances[gID + i];
+                    info->fGlyphWidths->fAdvance.append(1, &advance);
+                }
+            }
+            finishRange(info->fGlyphWidths.get(), face->num_glyphs - 1,
+                        SkAdvancedTypefaceMetrics::WidthRange::kRange);
+        } else {
+            info->fGlyphWidths.reset(
+                getAdvanceData(face,
+                               face->num_glyphs,
+                               glyphIDs,
+                               glyphIDsCount,
+                               &getWidthAdvance));
+        }
+    }
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kVAdvance_PerGlyphInfo &&
+            FT_HAS_VERTICAL(face)) {
+        SkASSERT(false);  // Not implemented yet.
+    }
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo &&
+            info->fType == SkAdvancedTypefaceMetrics::kType1_Font) {
+        // Postscript fonts may contain more than 255 glyphs, so we end up
+        // using multiple font descriptions with a glyph ordering.  Record
+        // the name of each glyph.
+        info->fGlyphNames.reset(
+                new SkAutoTArray<SkString>(face->num_glyphs));
+        for (int gID = 0; gID < face->num_glyphs; gID++) {
+            char glyphName[128];  // PS limit for names is 127 bytes.
+            FT_Get_Glyph_Name(face, gID, glyphName, 128);
+            info->fGlyphNames->get()[gID].set(glyphName);
+        }
+    }
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo &&
+           info->fType != SkAdvancedTypefaceMetrics::kType1_Font &&
+           face->num_charmaps) {
+        populate_glyph_to_unicode(face, &(info->fGlyphToUnicode));
+    }
+
+    if (!canEmbed(face))
+        info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+
+    unref_ft_face(face);
+    return info;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#define BLACK_LUMINANCE_LIMIT   0x40
+#define WHITE_LUMINANCE_LIMIT   0xA0
+
+static bool bothZero(SkScalar a, SkScalar b) {
+    return 0 == a && 0 == b;
+}
+
+// returns false if there is any non-90-rotation or skew
+static bool isAxisAligned(const SkScalerContext::Rec& rec) {
+    return 0 == rec.fPreSkewX &&
+           (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
+            bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+    if (!gLCDSupportValid) {
+        InitFreetype();
+        FT_Done_FreeType(gFTLibrary);
+    }
+
+    if (!gLCDSupport && isLCD(*rec)) {
+        // If the runtime Freetype library doesn't support LCD mode, we disable
+        // it here.
+        rec->fMaskFormat = SkMask::kA8_Format;
+    }
+
+    SkPaint::Hinting h = rec->getHinting();
+    if (SkPaint::kFull_Hinting == h && !isLCD(*rec)) {
+        // collapse full->normal hinting if we're not doing LCD
+        h = SkPaint::kNormal_Hinting;
+    }
+    if ((rec->fFlags & SkScalerContext::kSubpixelPositioning_Flag) || isLCD(*rec)) {
+        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);
+    }
+#endif
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+uint32_t SkFontHost::GetUnitsPerEm(SkFontID fontID) {
+    SkAutoMutexAcquire ac(gFTMutex);
+    FT_Library libInit = NULL;
+    if (gFTCount == 0) {
+        if (!InitFreetype())
+            sk_throw();
+        libInit = gFTLibrary;
+    }
+    SkAutoTCallIProc<struct FT_LibraryRec_, FT_Done_FreeType> ftLib(libInit);
+    SkFaceRec *rec = ref_ft_face(fontID);
+    uint16_t unitsPerEm = 0;
+
+    if (rec != NULL && rec->fFace != NULL) {
+        unitsPerEm = rec->fFace->units_per_EM;
+        unref_ft_face(rec->fFace);
+    }
+
+    return (uint32_t)unitsPerEm;
+}
+#endif
+
+SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
+        : SkScalerContext(desc) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    if (gFTCount == 0) {
+        if (!InitFreetype()) {
+            sk_throw();
+        }
+        SkFontHost::GetGammaTables(gGammaTables);
+    }
+    ++gFTCount;
+
+    // load the font file
+    fFTSize = NULL;
+    fFace = NULL;
+    fFaceRec = ref_ft_face(fRec.fFontID);
+    if (NULL == fFaceRec) {
+        return;
+    }
+    fFace = fFaceRec->fFace;
+
+    // compute our factors from the record
+
+    SkMatrix    m;
+
+    fRec.getSingleMatrix(&m);
+
+#ifdef DUMP_STRIKE_CREATION
+    SkString     keyString;
+    SkFontHost::GetDescriptorKeyString(desc, &keyString);
+    printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize),
+           SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX),
+           SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]),
+           SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]),
+           fRec.getHinting(), fRec.fMaskFormat, keyString.c_str());
+#endif
+
+    //  now compute our scale factors
+    SkScalar    sx = m.getScaleX();
+    SkScalar    sy = m.getScaleY();
+
+    if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) {
+        // sort of give up on hinting
+        sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX()));
+        sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy));
+        sx = sy = SkScalarAve(sx, sy);
+
+        SkScalar inv = SkScalarInvert(sx);
+
+        // flip the skew elements to go from our Y-down system to FreeType's
+        fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv));
+        fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv));
+        fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv));
+        fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv));
+    } else {
+        fMatrix22.xx = fMatrix22.yy = SK_Fixed1;
+        fMatrix22.xy = fMatrix22.yx = 0;
+    }
+
+    fScaleX = SkScalarToFixed(sx);
+    fScaleY = SkScalarToFixed(sy);
+
+    // compute the flags we send to Load_Glyph
+    fUseVertMetrics = false;
+    {
+        FT_Int32 loadFlags = FT_LOAD_DEFAULT;
+        bool linearMetrics = false;
+
+        if (SkMask::kBW_Format == fRec.fMaskFormat) {
+            // See http://code.google.com/p/chromium/issues/detail?id=43252#c24
+            loadFlags = FT_LOAD_TARGET_MONO;
+            if (fRec.getHinting() == SkPaint::kNo_Hinting) {
+                loadFlags = FT_LOAD_NO_HINTING;
+                linearMetrics = true;
+            }
+        } else {
+            switch (fRec.getHinting()) {
+            case SkPaint::kNo_Hinting:
+                loadFlags = FT_LOAD_NO_HINTING;
+                linearMetrics = true;
+                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)
+                    loadFlags = FT_LOAD_FORCE_AUTOHINT;
+                else
+                    loadFlags = FT_LOAD_NO_AUTOHINT;
+                break;
+            case SkPaint::kFull_Hinting:
+                if (fRec.fFlags & SkScalerContext::kAutohinting_Flag) {
+                    loadFlags = FT_LOAD_FORCE_AUTOHINT;
+                    break;
+                }
+                loadFlags = FT_LOAD_TARGET_NORMAL;
+                if (isLCD(fRec)) {
+                    if (fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag) {
+                        loadFlags = FT_LOAD_TARGET_LCD_V;
+                    } else {
+                        loadFlags = FT_LOAD_TARGET_LCD;
+                    }
+                }
+                break;
+            default:
+                SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting());
+                break;
+            }
+        }
+
+        if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) {
+            loadFlags |= FT_LOAD_NO_BITMAP;
+        }
+
+        // Always using FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH to get correct
+        // advances, as fontconfig and cairo do.
+        // 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)) {
+            loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+            fUseVertMetrics = true;
+        }
+
+        fLoadGlyphFlags = loadFlags;
+        fDoLinearMetrics = linearMetrics;
+    }
+
+    // now create the FT_Size
+
+    {
+        FT_Error    err;
+
+        err = FT_New_Size(fFace, &fFTSize);
+        if (err != 0) {
+            SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n",
+                        fFaceRec->fFontID, fScaleX, fScaleY, err));
+            fFace = NULL;
+            return;
+        }
+
+        err = FT_Activate_Size(fFTSize);
+        if (err != 0) {
+            SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
+                        fFaceRec->fFontID, fScaleX, fScaleY, err));
+            fFTSize = NULL;
+        }
+
+        err = FT_Set_Char_Size( fFace,
+                                SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY),
+                                72, 72);
+        if (err != 0) {
+            SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
+                        fFaceRec->fFontID, fScaleX, fScaleY, err));
+            fFace = NULL;
+            return;
+        }
+
+        FT_Set_Transform( fFace, &fMatrix22, NULL);
+    }
+}
+
+SkScalerContext_FreeType::~SkScalerContext_FreeType() {
+    if (fFTSize != NULL) {
+        FT_Done_Size(fFTSize);
+    }
+
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    if (fFace != NULL) {
+        unref_ft_face(fFace);
+    }
+    if (--gFTCount == 0) {
+//        SkDEBUGF(("FT_Done_FreeType\n"));
+        FT_Done_FreeType(gFTLibrary);
+        SkDEBUGCODE(gFTLibrary = NULL;)
+    }
+}
+
+/*  We call this before each use of the fFace, since we may be sharing
+    this face with other context (at different sizes).
+*/
+FT_Error SkScalerContext_FreeType::setupSize() {
+    FT_Error    err = FT_Activate_Size(fFTSize);
+
+    if (err != 0) {
+        SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
+                    fFaceRec->fFontID, fScaleX, fScaleY, err));
+        fFTSize = NULL;
+    } else {
+        // seems we need to reset this every time (not sure why, but without it
+        // I get random italics from some other fFTSize)
+        FT_Set_Transform( fFace, &fMatrix22, NULL);
+    }
+    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;
+}
+
+uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) {
+    return SkToU16(FT_Get_Char_Index( fFace, uni ));
+}
+
+SkUnichar SkScalerContext_FreeType::generateGlyphToChar(uint16_t glyph) {
+    // iterate through each cmap entry, looking for matching glyph indices
+    FT_UInt glyphIndex;
+    SkUnichar charCode = FT_Get_First_Char( fFace, &glyphIndex );
+
+    while (glyphIndex != 0) {
+        if (glyphIndex == glyph) {
+            return charCode;
+        }
+        charCode = FT_Get_Next_Char( fFace, charCode, &glyphIndex );
+    }
+
+    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
+    * which are very cheap to compute with some font formats...
+    */
+    if (fDoLinearMetrics) {
+        SkAutoMutexAcquire  ac(gFTMutex);
+
+        if (this->setupSize()) {
+            glyph->zeroMetrics();
+            return;
+        }
+
+        FT_Error    error;
+        FT_Fixed    advance;
+
+        error = FT_Get_Advance( fFace, glyph->getGlyphID(fBaseGlyphCount),
+                                fLoadGlyphFlags | FT_ADVANCE_FLAG_FAST_ONLY,
+                                &advance );
+        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);
+            return;
+        }
+    }
+#endif /* FT_ADVANCES_H */
+    /* otherwise, we need to load/hint the glyph, which is slower */
+    this->generateMetrics(glyph);
+    return;
+}
+
+void SkScalerContext_FreeType::getBBoxForCurrentGlyph(SkGlyph* glyph,
+                                                      FT_BBox* bbox,
+                                                      bool snapToPixelBoundary) {
+
+    FT_Outline_Get_CBox(&fFace->glyph->outline, bbox);
+
+    if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
+        int dx = FixedToDot6(glyph->getSubXFixed());
+        int dy = FixedToDot6(glyph->getSubYFixed());
+        // negate dy since freetype-y-goes-up and skia-y-goes-down
+        bbox->xMin += dx;
+        bbox->yMin -= dy;
+        bbox->xMax += dx;
+        bbox->yMax -= dy;
+    }
+
+    // outset the box to integral boundaries
+    if (snapToPixelBoundary) {
+        bbox->xMin &= ~63;
+        bbox->yMin &= ~63;
+        bbox->xMax  = (bbox->xMax + 63) & ~63;
+        bbox->yMax  = (bbox->yMax + 63) & ~63;
+    }
+
+    // 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) {
+        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);
+        bbox->xMin += vector.x;
+        bbox->xMax += vector.x;
+        bbox->yMin += vector.y;
+        bbox->yMax += vector.y;
+    }
+}
+
+void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) {
+    if (isLCD(fRec)) {
+        glyph->fWidth += gLCDExtra;
+        glyph->fLeft -= gLCDExtra >> 1;
+    }
+}
+
+void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+
+    FT_Error    err;
+
+    if (this->setupSize()) {
+        goto ERROR;
+    }
+
+    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));
+    ERROR:
+        glyph->zeroMetrics();
+        return;
+    }
+
+    SkFixed vLeft, vTop;
+
+    switch ( fFace->glyph->format ) {
+      case FT_GLYPH_FORMAT_OUTLINE: {
+        FT_BBox bbox;
+
+        if (0 == fFace->glyph->outline.n_contours) {
+            glyph->fWidth = 0;
+            glyph->fHeight = 0;
+            glyph->fTop = 0;
+            glyph->fLeft = 0;
+            break;
+        }
+
+        if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+            emboldenOutline(&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);
+        }
+
+        updateGlyphIfLCD(glyph);
+
+        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);
+        }
+
+        if (fUseVertMetrics) {
+            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);
+            fFace->glyph->bitmap_left += SkFDot6Floor(vector.x);
+            fFace->glyph->bitmap_top  += SkFDot6Floor(vector.y);
+        }
+
+        glyph->fWidth   = SkToU16(fFace->glyph->bitmap.width);
+        glyph->fHeight  = SkToU16(fFace->glyph->bitmap.rows);
+        glyph->fTop     = -SkToS16(fFace->glyph->bitmap_top);
+        glyph->fLeft    = SkToS16(fFace->glyph->bitmap_left);
+        break;
+
+      default:
+        SkDEBUGFAIL("unknown glyph format");
+        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 (fDoLinearMetrics) {
+            glyph->fAdvanceX = -SkFixedMul(fMatrix22.xy, fFace->glyph->linearVertAdvance);
+            glyph->fAdvanceY = SkFixedMul(fMatrix22.yy, fFace->glyph->linearVertAdvance);
+        } 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);
+            }
+        }
+
+        // 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);
+    }
+
+
+#ifdef ENABLE_GLYPH_SPEW
+    SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY));
+    SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, glyph->fWidth));
+#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);
+
+    FT_Error    err;
+
+    if (this->setupSize()) {
+        goto ERROR;
+    }
+
+    err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags);
+    if (err != 0) {
+        SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n",
+                    glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err));
+    ERROR:
+        memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
+        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
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    SkASSERT(&glyph && path);
+
+    if (this->setupSize()) {
+        path->reset();
+        return;
+    }
+
+    uint32_t flags = fLoadGlyphFlags;
+    flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
+    flags &= ~FT_LOAD_RENDER;   // don't scan convert (we just want the outline)
+
+    FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags);
+
+    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;
+    }
+
+    if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+        emboldenOutline(&fFace->glyph->outline);
+    }
+
+    if (fUseVertMetrics) {
+        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);
+    }
+
+    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,
+                                                   SkPaint::FontMetrics* my) {
+    if (NULL == mx && NULL == my) {
+        return;
+    }
+
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    if (this->setupSize()) {
+        ERROR:
+        if (mx) {
+            sk_bzero(mx, sizeof(SkPaint::FontMetrics));
+        }
+        if (my) {
+            sk_bzero(my, sizeof(SkPaint::FontMetrics));
+        }
+        return;
+    }
+
+    FT_Face face = fFace;
+    int upem = face->units_per_EM;
+    if (upem <= 0) {
+        goto ERROR;
+    }
+
+    SkPoint pts[6];
+    SkFixed ys[6];
+    SkFixed scaleY = fScaleY;
+    SkFixed mxy = fMatrix22.xy;
+    SkFixed myy = fMatrix22.yy;
+    SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem;
+    SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem;
+
+    int leading = face->height - (face->ascender + -face->descender);
+    if (leading < 0) {
+        leading = 0;
+    }
+
+    // Try to get the OS/2 table from the font. This contains the specific
+    // average font width metrics which Windows uses.
+    TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+
+    ys[0] = -face->bbox.yMax;
+    ys[1] = -face->ascender;
+    ys[2] = -face->descender;
+    ys[3] = -face->bbox.yMin;
+    ys[4] = leading;
+    ys[5] = os2 ? os2->xAvgCharWidth : 0;
+
+    SkScalar x_height;
+    if (os2 && os2->sxHeight) {
+        x_height = SkFixedToScalar(SkMulDiv(fScaleX, os2->sxHeight, upem));
+    } else {
+        const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x');
+        if (x_glyph) {
+            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);
+            }
+            FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
+            x_height = SkFixedToScalar(SkFDot6ToFixed(bbox.yMax));
+        } else {
+            x_height = 0;
+        }
+    }
+
+    // convert upem-y values into scalar points
+    for (int i = 0; i < 6; i++) {
+        SkFixed y = SkMulDiv(scaleY, ys[i], upem);
+        SkFixed x = SkFixedMul(mxy, y);
+        y = SkFixedMul(myy, y);
+        pts[i].set(SkFixedToScalar(x), SkFixedToScalar(y));
+    }
+
+    if (mx) {
+        mx->fTop = pts[0].fX;
+        mx->fAscent = pts[1].fX;
+        mx->fDescent = pts[2].fX;
+        mx->fBottom = pts[3].fX;
+        mx->fLeading = pts[4].fX;
+        mx->fAvgCharWidth = pts[5].fX;
+        mx->fXMin = xmin;
+        mx->fXMax = xmax;
+        mx->fXHeight = x_height;
+    }
+    if (my) {
+        my->fTop = pts[0].fY;
+        my->fAscent = pts[1].fY;
+        my->fDescent = pts[2].fY;
+        my->fBottom = pts[3].fY;
+        my->fLeading = pts[4].fY;
+        my->fAvgCharWidth = pts[5].fY;
+        my->fXMin = xmin;
+        my->fXMax = xmax;
+        my->fXHeight = x_height;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    SkScalerContext_FreeType* c = SkNEW_ARGS(SkScalerContext_FreeType, (desc));
+    if (!c->success()) {
+        SkDELETE(c);
+        c = NULL;
+    }
+    return c;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  Export this so that other parts of our FonttHost port can make use of our
+    ability to extract the name+style from a stream, using FreeType's api.
+*/
+bool find_name_and_attributes(SkStream* stream, SkString* name,
+                              SkTypeface::Style* style, bool* isFixedWidth) {
+    FT_Library  library;
+    if (FT_Init_FreeType(&library)) {
+        return false;
+    }
+
+    FT_Open_Args    args;
+    memset(&args, 0, sizeof(args));
+
+    const void* memoryBase = stream->getMemoryBase();
+    FT_StreamRec    streamRec;
+
+    if (NULL != memoryBase) {
+        args.flags = FT_OPEN_MEMORY;
+        args.memory_base = (const FT_Byte*)memoryBase;
+        args.memory_size = stream->getLength();
+    } else {
+        memset(&streamRec, 0, sizeof(streamRec));
+        streamRec.size = stream->read(NULL, 0);
+        streamRec.descriptor.pointer = stream;
+        streamRec.read  = sk_stream_read;
+        streamRec.close = sk_stream_close;
+
+        args.flags = FT_OPEN_STREAM;
+        args.stream = &streamRec;
+    }
+
+    FT_Face face;
+    if (FT_Open_Face(library, &args, 0, &face)) {
+        FT_Done_FreeType(library);
+        return false;
+    }
+
+    int tempStyle = SkTypeface::kNormal;
+    if (face->style_flags & FT_STYLE_FLAG_BOLD) {
+        tempStyle |= SkTypeface::kBold;
+    }
+    if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
+        tempStyle |= SkTypeface::kItalic;
+    }
+
+    if (name) {
+        name->set(face->family_name);
+    }
+    if (style) {
+        *style = (SkTypeface::Style) tempStyle;
+    }
+    if (isFixedWidth) {
+        *isFixedWidth = FT_IS_FIXED_WIDTH(face);
+    }
+
+    FT_Done_Face(face);
+    FT_Done_FreeType(library);
+    return true;
+}
diff --git a/legacy/src/ports/SkFontHost_android.cpp b/legacy/src/ports/SkFontHost_android.cpp
new file mode 100644
index 0000000..dddadd0
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_android.cpp
@@ -0,0 +1,1231 @@
+/* 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.
+*/
+
+#include "SkFontHost.h"
+#include "SkGraphics.h"
+#include "SkDescriptor.h"
+#include "SkMMapStream.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTSearch.h"
+#include "FontHostConfiguration_android.h"
+#include <stdio.h>
+#include <string.h>
+#include "SkGlyphCache.h"
+#include "SkLanguage.h"
+#include "SkTypeface_android.h"
+#include "SkTArray.h"
+#include "SkTDict.h"
+#include "SkTSearch.h"
+
+//#define SkDEBUGF(args       )       SkDebugf args
+
+#ifndef SK_FONT_FILE_PREFIX
+    #define SK_FONT_FILE_PREFIX          "/fonts/"
+#endif
+
+// Defined in SkFontHost_FreeType.cpp
+bool find_name_and_attributes(SkStream* stream, SkString* name,
+                              SkTypeface::Style* style, bool* isFixedWidth);
+
+static void getFullPathForSysFonts(SkString* full, const char name[]) {
+    full->set(getenv("ANDROID_ROOT"));
+    full->append(SK_FONT_FILE_PREFIX);
+    full->append(name);
+}
+
+static bool getNameAndStyle(const char path[], SkString* name,
+                               SkTypeface::Style* style,
+                               bool* isFixedWidth, bool isExpected) {
+    SkString        fullpath;
+    getFullPathForSysFonts(&fullpath, path);
+
+    SkMMAPStream stream(fullpath.c_str());
+    if (stream.getLength() > 0) {
+        return find_name_and_attributes(&stream, name, style, isFixedWidth);
+    }
+    else {
+        SkFILEStream stream(fullpath.c_str());
+        if (stream.getLength() > 0) {
+            return find_name_and_attributes(&stream, name, style, isFixedWidth);
+        }
+    }
+
+    if (isExpected) {
+        SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
+    }
+    return false;
+}
+
+static SkTypeface* deserializeLocked(SkStream* stream);
+static SkTypeface* createTypefaceLocked(const SkTypeface* familyFace,
+        const char familyName[], const void* data, size_t bytelength,
+        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);
+static SkTypeface* createTypefaceFromStreamLocked(SkStream* stream);
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct FamilyRec;
+
+/*  This guy holds a mapping of a name -> family, used for looking up fonts.
+    Since it is stored in a stretchy array that doesn't preserve object
+    semantics, we don't use constructor/destructors, but just have explicit
+    helpers to manage our internal bookkeeping.
+*/
+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);
+        fFamily = family;   // we don't own this, so just record the reference
+    }
+
+    void destruct() {
+        free((char*)fName);
+        // we don't own family, so just ignore our reference
+    }
+};
+typedef SkTDArray<NameFamilyPair> NameFamilyPairList;
+
+// we use atomic_inc to grow this for each typeface we create
+static int32_t gUniqueFontID;
+
+// this is the mutex that protects all of the global data structures in this module
+// functions with the Locked() suffix must be called while holding this mutex
+SK_DECLARE_STATIC_MUTEX(gFamilyHeadAndNameListMutex);
+static FamilyRec* gFamilyHead = NULL;
+static SkTDArray<NameFamilyPair> gFallbackFilenameList;
+static NameFamilyPairList gNameList;
+
+struct FamilyRec {
+    FamilyRec*  fNext;
+    SkTypeface* fFaces[4];
+
+    FamilyRec() : fNext(NULL) {
+        memset(fFaces, 0, sizeof(fFaces));
+    }
+};
+
+static SkTypeface* findBestFaceLocked(const FamilyRec* family,
+                                  SkTypeface::Style style) {
+    SkTypeface* const* faces = family->fFaces;
+
+    if (faces[style] != NULL) { // exact match
+        return faces[style];
+    }
+    // look for a matching bold
+    style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
+    if (faces[style] != NULL) {
+        return faces[style];
+    }
+    // look for the plain
+    if (faces[SkTypeface::kNormal] != NULL) {
+        return faces[SkTypeface::kNormal];
+    }
+    // look for anything
+    for (int i = 0; i < 4; i++) {
+        if (faces[i] != NULL) {
+            return faces[i];
+        }
+    }
+    // should never get here, since the faces list should not be empty
+    SkDEBUGFAIL("faces list is empty");
+    return NULL;
+}
+
+static SkTypeface* FindBestFace(const FamilyRec* family,
+            SkTypeface::Style style) {
+    SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
+    return findBestFaceLocked(family, style);
+}
+
+static FamilyRec* findFamilyLocked(const SkTypeface* member) {
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            if (curr->fFaces[i] == member) {
+                return curr;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+/*  Returns the matching typeface, or NULL. If a typeface is found, its refcnt
+    is not modified.
+ */
+static SkTypeface* findFromUniqueIDLocked(uint32_t uniqueID) {
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            SkTypeface* face = curr->fFaces[i];
+            if (face != NULL && face->uniqueID() == uniqueID) {
+                return face;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+/*  Returns the matching typeface, or NULL. If a typeface is found, its refcnt
+    is not modified.
+ */
+static SkTypeface* FindFromUniqueID(uint32_t uniqueID) {
+    SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
+    return findFromUniqueIDLocked(uniqueID);
+}
+
+/*  Remove reference to this face from its family. If the resulting family
+    is empty (has no faces), return that family, otherwise return NULL
+*/
+static FamilyRec* removeFromFamilyLocked(const SkTypeface* face) {
+    FamilyRec* family = findFamilyLocked(face);
+    if (family) {
+        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;
+            }
+        }
+    } else {
+//        SkDebugf("removeFromFamilyLocked(%p) face not found", face);
+    }
+    return family;  // return the empty family
+}
+
+// maybe we should make FamilyRec be doubly-linked
+static void detachAndDeleteFamilyLocked(FamilyRec* family) {
+    FamilyRec* curr = gFamilyHead;
+    FamilyRec* prev = NULL;
+
+    while (curr != NULL) {
+        FamilyRec* next = curr->fNext;
+        if (curr == family) {
+            if (prev == NULL) {
+                gFamilyHead = next;
+            } else {
+                prev->fNext = next;
+            }
+            SkDELETE(family);
+            return;
+        }
+        prev = curr;
+        curr = next;
+    }
+    SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
+}
+
+static SkTypeface* findTypefaceLocked(const char name[], SkTypeface::Style style) {
+    int count = gNameList.count();
+    NameFamilyPair* list = gNameList.begin();
+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
+    if (index >= 0) {
+        return findBestFaceLocked(list[index].fFamily, style);
+    }
+    return NULL;
+}
+
+static SkTypeface* findTypefaceLocked(const SkTypeface* familyMember,
+                                 SkTypeface::Style style) {
+    const FamilyRec* family = findFamilyLocked(familyMember);
+    return family ? findBestFaceLocked(family, style) : NULL;
+}
+
+static void addNameLocked(const char name[], FamilyRec* family) {
+    SkAutoAsciiToLC tolc(name);
+    name = tolc.lc();
+
+    int count = gNameList.count();
+    NameFamilyPair* list = gNameList.begin();
+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
+    if (index < 0) {
+        list = gNameList.insert(~index);
+        list->construct(name, family);
+    }
+}
+
+static void removeFromNamesLocked(FamilyRec* emptyFamily) {
+#ifdef SK_DEBUG
+    for (int i = 0; i < 4; i++) {
+        SkASSERT(emptyFamily->fFaces[i] == NULL);
+    }
+#endif
+
+    // must go backwards when removing
+    for (int i = gNameList.count() - 1; i >= 0; --i) {
+        NameFamilyPair& pair = gNameList[i];
+        if (pair.fFamily == emptyFamily) {
+            pair.destruct();
+            gNameList.remove(i);
+        }
+    }
+}
+
+static void addTypefaceLocked(SkTypeface* typeface, SkTypeface* familyMember) {
+    FamilyRec* rec = NULL;
+    if (familyMember) {
+        rec = findFamilyLocked(familyMember);
+        SkASSERT(rec);
+    } else {
+        rec = SkNEW(FamilyRec);
+        rec->fNext = gFamilyHead;
+        gFamilyHead = rec;
+    }
+    rec->fFaces[typeface->style()] = typeface;
+}
+
+static void removeTypeface(SkTypeface* typeface) {
+    SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
+
+    // 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 = removeFromFamilyLocked(typeface);
+    if (NULL != family) {
+        removeFromNamesLocked(family);
+        detachAndDeleteFamilyLocked(family);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FamilyTypeface : public SkTypeface {
+protected:
+    FamilyTypeface(Style style, bool sysFont, bool isFixedWidth)
+    : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) {
+        fIsSysFont = sysFont;
+    }
+
+public:
+    virtual ~FamilyTypeface() {
+        removeTypeface(this);
+    }
+
+    bool isSysFont() const { return fIsSysFont; }
+
+    virtual SkStream* openStream() = 0;
+    virtual const char* getUniqueString() const = 0;
+    virtual const char* getFilePath() const = 0;
+
+private:
+    bool    fIsSysFont;
+
+    typedef SkTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTypeface : public FamilyTypeface {
+public:
+    StreamTypeface(Style style, bool sysFont, SkStream* stream, bool isFixedWidth)
+    : INHERITED(style, sysFont, isFixedWidth) {
+        SkASSERT(stream);
+        stream->ref();
+        fStream = stream;
+    }
+
+    virtual ~StreamTypeface() {
+        fStream->unref();
+    }
+
+    // overrides
+    virtual SkStream* openStream() {
+        // we just ref our existing stream, since the caller will call unref()
+        // when they are through
+        fStream->ref();
+        // must rewind each time, since the caller assumes a "new" stream
+        fStream->rewind();
+        return fStream;
+    }
+    virtual const char* getUniqueString() const { return NULL; }
+    virtual const char* getFilePath() const { return NULL; }
+
+private:
+    SkStream* fStream;
+
+    typedef FamilyTypeface INHERITED;
+};
+
+class FileTypeface : public FamilyTypeface {
+public:
+    FileTypeface(Style style, bool sysFont, const char path[], bool isFixedWidth)
+    : INHERITED(style, sysFont, 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);
+            // maybe MMAP isn't supported. try FILE
+            stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
+            if (stream->getLength() <= 0) {
+                SkDELETE(stream);
+                stream = NULL;
+            }
+        }
+        return stream;
+    }
+    virtual const char* getUniqueString() const {
+        const char* str = strrchr(fPath.c_str(), '/');
+        if (str) {
+            str += 1;   // skip the '/'
+        }
+        return str;
+    }
+    virtual const char* getFilePath() const {
+        return fPath.c_str();
+    }
+
+private:
+    SkString fPath;
+
+    typedef FamilyTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+// used to record our notion of the pre-existing fonts
+struct FontInitRec {
+    const char*          fFileName;
+    const char* const*   fNames;     // null-terminated list
+    SkPaint::FontVariant fVariant;
+    SkLanguage           fLanguage;
+};
+
+//used to record information about the fallback fonts
+struct FallbackFontRec {
+    SkFontID             fFontID;
+    SkPaint::FontVariant fVariant;
+};
+
+struct FallbackFontList {
+    FallbackFontList(const SkLanguage& language) : fLanguage(language) { }
+    SkTDArray<FallbackFontRec> fList;
+    SkLanguage                 fLanguage;
+};
+
+// deliberately empty, but we use the address to identify fallback fonts
+static const char* gFBNames[] = { NULL };
+
+/*  Fonts are grouped by family, with the first font in a family having the
+    list of names (even if that list is empty), and the following members having
+    null for the list. The names list must be NULL-terminated.
+*/
+static SkTArray<FontInitRec> gSystemFonts;
+static SkTDArray<FallbackFontList*> gFallbackFontLists;
+
+// these globals are assigned (once) by loadSystemFontsLocked()
+static FamilyRec* gDefaultFamily = NULL;
+static SkTypeface* gDefaultNormal = NULL;
+static char** gDefaultNames = NULL;
+
+static FallbackFontList* getFallbackFontListLocked(const SkLanguage& lang);
+static void dumpGlobalsLocked() {
+    SkDebugf("gDefaultNormal=%p id=%u refCnt=%d", gDefaultNormal,
+             gDefaultNormal ? gDefaultNormal->uniqueID() : 0,
+             gDefaultNormal ? gDefaultNormal->getRefCnt() : 0);
+
+    if (gDefaultFamily) {
+        SkDebugf("gDefaultFamily=%p fFaces={%u,%u,%u,%u} refCnt={%d,%d,%d,%d}",
+                 gDefaultFamily,
+                 gDefaultFamily->fFaces[0] ? gDefaultFamily->fFaces[0]->uniqueID() : 0,
+                 gDefaultFamily->fFaces[1] ? gDefaultFamily->fFaces[1]->uniqueID() : 0,
+                 gDefaultFamily->fFaces[2] ? gDefaultFamily->fFaces[2]->uniqueID() : 0,
+                 gDefaultFamily->fFaces[3] ? gDefaultFamily->fFaces[3]->uniqueID() : 0,
+                 gDefaultFamily->fFaces[0] ? gDefaultFamily->fFaces[0]->getRefCnt() : 0,
+                 gDefaultFamily->fFaces[1] ? gDefaultFamily->fFaces[1]->getRefCnt() : 0,
+                 gDefaultFamily->fFaces[2] ? gDefaultFamily->fFaces[2]->getRefCnt() : 0,
+                 gDefaultFamily->fFaces[3] ? gDefaultFamily->fFaces[3]->getRefCnt() : 0);
+    } else {
+        SkDebugf("gDefaultFamily=%p", gDefaultFamily);
+    }
+
+    FallbackFontList* defaultFallbackList =
+            getFallbackFontListLocked(SkLanguage());
+    SkASSERT(defaultFallbackList != NULL);
+    SkDebugf("gSystemFonts.count()=%d defaultFallbackList->fList.count()=%d",
+           gSystemFonts.count(), defaultFallbackList->fList.count());
+
+    for (int i = 0; i < gSystemFonts.count(); ++i) {
+        SkDebugf("gSystemFonts[%d] fileName=%s", i, gSystemFonts[i].fFileName);
+        size_t namesIndex = 0;
+        if (gSystemFonts[i].fNames)
+            for (const char* fontName = gSystemFonts[i].fNames[namesIndex];
+                    fontName != 0;
+                    fontName = gSystemFonts[i].fNames[++namesIndex]) {
+                SkDebugf("       name[%u]=%s", namesIndex, fontName);
+            }
+    }
+
+    if (gFamilyHead) {
+        FamilyRec* rec = gFamilyHead;
+        int i=0;
+        while (rec) {
+            SkDebugf("gFamilyHead[%d]=%p fFaces={%u,%u,%u,%u} refCnt={%d,%d,%d,%d}",
+                     i++, rec,
+                     rec->fFaces[0] ? rec->fFaces[0]->uniqueID() : 0,
+                     rec->fFaces[1] ? rec->fFaces[1]->uniqueID() : 0,
+                     rec->fFaces[2] ? rec->fFaces[2]->uniqueID() : 0,
+                     rec->fFaces[3] ? rec->fFaces[3]->uniqueID() : 0,
+                     rec->fFaces[0] ? rec->fFaces[0]->getRefCnt() : 0,
+                     rec->fFaces[1] ? rec->fFaces[1]->getRefCnt() : 0,
+                     rec->fFaces[2] ? rec->fFaces[2]->getRefCnt() : 0,
+                     rec->fFaces[3] ? rec->fFaces[3]->getRefCnt() : 0);
+            rec = rec->fNext;
+        }
+    } else {
+        SkDebugf("gFamilyHead=%p", gFamilyHead);
+    }
+
+}
+
+
+static bool haveSystemFont(const char* filename) {
+    for (int i = 0; i < gSystemFonts.count(); i++) {
+        if (strcmp(gSystemFonts[i].fFileName, filename) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// (SkLanguage)<->(fallback chain index) translation
+static const size_t kLangDictSize = 128;
+static SkTDict<FallbackFontList*> gLangTagToFallbackFontList(kLangDictSize);
+static bool gIsOKToUseFallbackFontListCache = false;
+
+// crawl fallback font lists by hand looking for a specific language
+static FallbackFontList* getFallbackFontListNoCacheLocked(
+        const SkLanguage& lang) {
+    unsigned int numLists = gFallbackFontLists.count();
+    for (unsigned int listIdx = 0; listIdx < numLists; ++listIdx) {
+        FallbackFontList* list = gFallbackFontLists[listIdx];
+        SkASSERT(list != NULL);
+        if (list->fLanguage == lang) {
+            return list;
+        }
+    }
+    return NULL;
+}
+
+// perform fancy fuzzy-matching memoized query for a fallback font list.
+// should only be called after fallback font lists are fully loaded.
+static FallbackFontList* getFallbackFontListLocked(const SkLanguage& lang) {
+    SkASSERT(gIsOKToUseFallbackFontListCache);
+    const SkString& langTag = lang.getTag();
+    FallbackFontList* fallbackFontList;
+    if (gLangTagToFallbackFontList.find(langTag.c_str(), langTag.size(),
+            &fallbackFontList)) {
+        // cache hit!
+        return fallbackFontList;
+    }
+
+    // try again without the cache
+    fallbackFontList = getFallbackFontListNoCacheLocked(lang);
+    if (fallbackFontList != NULL) {
+        // found it - cache and return
+        gLangTagToFallbackFontList.set(langTag.c_str(), langTag.size(),
+                fallbackFontList);
+        SkDEBUGF(("new fallback cache entry: \"%s\"", langTag.c_str()));
+        return fallbackFontList;
+    }
+
+    // no hit - can we fuzzy-match?
+    if (lang.getTag().isEmpty()) {
+        // nope! this happens if attempting to direct match with no default
+        return NULL;
+    }
+
+    // attempt fuzzy match
+    SkLanguage parent = lang.getParent();
+    fallbackFontList = getFallbackFontListLocked(parent);
+    if (fallbackFontList != NULL) {
+        // found it - cache and return
+        gLangTagToFallbackFontList.set(langTag.c_str(), langTag.size(),
+                fallbackFontList);
+        SkDEBUGF(("new fallback cache entry: \"%s\" -> \"%s\"", langTag.c_str(),
+                fallbackFontList->fLanguage.getTag().c_str()));
+        return fallbackFontList;
+    }
+
+    // utter failure. this happens if attempting to fuzzy-match with no default
+    SkASSERT(fallbackFontList != NULL);
+    return NULL;
+}
+
+// creates a new fallback font list for the specified language
+static FallbackFontList* createFallbackFontListLocked(const SkLanguage& lang) {
+    SkASSERT(!gIsOKToUseFallbackFontListCache);
+    SkDEBUGF(("new fallback list: \"%s\"", lang.getTag().c_str()));
+    FallbackFontList* fallbackFontList = new FallbackFontList(lang);
+    gFallbackFontLists.push(fallbackFontList);
+    return fallbackFontList;
+}
+
+// adds a fallback font record to both the default fallback chain and the
+// language-specific fallback chain to which it belongs, if any
+static void addFallbackFontLocked(const FallbackFontRec& fallbackRec,
+        const SkLanguage& lang) {
+    SkASSERT(!gIsOKToUseFallbackFontListCache);
+    SkDEBUGF(("new fallback font: %d, in \"%s\"", fallbackRec.fFontID,
+            lang.getTag().c_str()));
+    // add to the default fallback list
+    FallbackFontList* fallbackList =
+            getFallbackFontListNoCacheLocked(SkLanguage());
+    if (fallbackList == NULL) {
+        // oops! no default list yet. create one.
+        fallbackList = createFallbackFontListLocked(SkLanguage());
+    }
+    SkASSERT(fallbackList != NULL);
+    fallbackList->fList.push(fallbackRec);
+    if (lang.getTag().isEmpty()) {
+        return;
+    }
+    // also add to the appropriate language's fallback list
+    fallbackList = getFallbackFontListNoCacheLocked(lang);
+    if (fallbackList == NULL) {
+        // first entry for this list!
+        fallbackList = createFallbackFontListLocked(lang);
+    }
+    SkASSERT(fallbackList != NULL);
+    fallbackList->fList.push(fallbackRec);
+}
+
+static int getSystemFontIndexForFontID(SkFontID fontID) {
+    // font unique id = one-based index in system font table
+    SkASSERT(fontID - 1 < gSystemFonts.count());
+    return fontID - 1;
+}
+
+// scans the default fallback font chain, adding every entry to every other
+// fallback font chain to which it does not belong. this results in every
+// language-specific fallback font chain having all of its fallback fonts at
+// the front of the chain, and everything else at the end. after this has been
+// run, it is ok to use the fallback font chain lookup table.
+static void finaliseFallbackFontListsLocked() {
+    SkASSERT(!gIsOKToUseFallbackFontListCache);
+    // if we have more than one list, we need to finalise non-default lists
+    unsigned int numLists = gFallbackFontLists.count();
+    if (numLists > 1) {
+        // pull fonts off of the default list...
+        FallbackFontList* defaultList = getFallbackFontListNoCacheLocked(
+                SkLanguage());
+        SkASSERT(defaultList != NULL);
+        int numDefaultFonts = defaultList->fList.count();
+        for (int fontIdx = 0; fontIdx < numDefaultFonts; ++fontIdx) {
+            // figure out which language they represent
+            SkFontID fontID = defaultList->fList[fontIdx].fFontID;
+            int sysFontIdx = getSystemFontIndexForFontID(fontID);
+            const SkLanguage& lang = gSystemFonts[sysFontIdx].fLanguage;
+            for (unsigned int listIdx = 0; listIdx < numLists; ++listIdx) {
+                // and add them to every other language's list
+                FallbackFontList* thisList = gFallbackFontLists[listIdx];
+                SkASSERT(thisList != NULL);
+                if (thisList != defaultList && thisList->fLanguage != lang) {
+                    thisList->fList.push(defaultList->fList[fontIdx]);
+                }
+            }
+        }
+    }
+    gIsOKToUseFallbackFontListCache = true;
+}
+
+static void resetFallbackFontListsLocked() {
+    // clear cache
+    gLangTagToFallbackFontList.reset();
+    // clear the data it pointed at
+    int numFallbackLists = gFallbackFontLists.count();
+    for (int fallbackIdx = 0; fallbackIdx < numFallbackLists; ++fallbackIdx) {
+        delete gFallbackFontLists[fallbackIdx];
+    }
+    gFallbackFontLists.reset();
+    gIsOKToUseFallbackFontListCache = false;
+}
+
+/*  Load info from a configuration file that populates the system/fallback font structures
+*/
+static void loadFontInfoLocked() {
+    resetFallbackFontListsLocked();
+
+    SkTDArray<FontFamily*> fontFamilies;
+    getFontFamilies(fontFamilies);
+
+    gSystemFonts.reset();
+
+    for (int i = 0; i < fontFamilies.count(); ++i) {
+        FontFamily *family = fontFamilies[i];
+        for (int j = 0; j < family->fFontFileArray.count(); ++j) {
+            const char* filename = family->fFontFileArray[j]->fFileName;
+            if (haveSystemFont(filename)) {
+                SkDebugf("---- system font and fallback font files specify a duplicate "
+                        "font %s, skipping the second occurrence", filename);
+                continue;
+            }
+
+            FontInitRec fontInfoRecord;
+            fontInfoRecord.fFileName = filename;
+            fontInfoRecord.fVariant = family->fFontFileArray[j]->fVariant;
+            fontInfoRecord.fLanguage = family->fFontFileArray[j]->fLanguage;
+            if (j == 0) {
+                if (family->fNames.count() == 0) {
+                    // Fallback font
+                    fontInfoRecord.fNames = (char **)gFBNames;
+                } else {
+                    SkTDArray<const char*> names = family->fNames;
+                    const char **nameList = (const char**)
+                            malloc((names.count() + 1) * sizeof(char*));
+                    if (nameList == NULL) {
+                        // shouldn't get here
+                        break;
+                    }
+                    if (gDefaultNames == NULL) {
+                        gDefaultNames = (char**) nameList;
+                    }
+                    for (int i = 0; i < names.count(); ++i) {
+                        nameList[i] = names[i];
+                    }
+                    nameList[names.count()] = NULL;
+                    fontInfoRecord.fNames = nameList;
+                }
+            } else {
+                fontInfoRecord.fNames = NULL;
+            }
+            gSystemFonts.push_back(fontInfoRecord);
+        }
+    }
+    fontFamilies.deleteAll();
+
+    SkDEBUGF(("---- We have %d system fonts", gSystemFonts.count()));
+    for (int i = 0; i < gSystemFonts.count(); ++i) {
+        SkDEBUGF(("---- gSystemFonts[%d] fileName=%s", i, gSystemFonts[i].fFileName));
+    }
+}
+
+/*
+ *  Called once (ensured by the sentinel check at the beginning of our body).
+ *  Initializes all the globals, and register the system fonts.
+ */
+static void initSystemFontsLocked() {
+    // check if we've already been called
+    if (gDefaultNormal) {
+        return;
+    }
+
+    SkASSERT(gUniqueFontID == 0);
+
+    loadFontInfoLocked();
+
+    SkTypeface* firstInFamily = NULL;
+    for (int i = 0; i < gSystemFonts.count(); i++) {
+        // if we're the first in a new family, clear firstInFamily
+        const char* const* names = gSystemFonts[i].fNames;
+        if (names != NULL) {
+            firstInFamily = NULL;
+        }
+
+        bool isFixedWidth;
+        SkString name;
+        SkTypeface::Style style;
+
+        // we expect all the fonts, except the "fallback" fonts
+        bool isExpected = (names != gFBNames);
+        if (!getNameAndStyle(gSystemFonts[i].fFileName, &name, &style,
+                &isFixedWidth, isExpected)) {
+            // We need to increase gUniqueFontID here so that the unique id of
+            // each font matches its index in gSystemFonts array, as expected
+            // by findUniqueIDLocked.
+            sk_atomic_inc(&gUniqueFontID);
+            continue;
+        }
+
+        SkString fullpath;
+        getFullPathForSysFonts(&fullpath, gSystemFonts[i].fFileName);
+
+        SkTypeface* tf = SkNEW_ARGS(FileTypeface, (style,
+                true,  // system-font (cannot delete)
+                fullpath.c_str(), // filename
+                isFixedWidth));
+        addTypefaceLocked(tf, firstInFamily);
+
+        SkDEBUGF(("---- SkTypeface[%d] %s fontID %d\n",
+                  i, gSystemFonts[i].fFileName, tf->uniqueID()));
+
+        if (names != NULL) {
+            // see if this is one of our fallback fonts
+            if (names == gFBNames) {
+                // add to appropriate fallback chains
+                FallbackFontRec fallbackRec;
+                fallbackRec.fFontID = tf->uniqueID();
+                fallbackRec.fVariant = gSystemFonts[i].fVariant;
+                addFallbackFontLocked(fallbackRec, gSystemFonts[i].fLanguage);
+            }
+
+            firstInFamily = tf;
+            FamilyRec* family = findFamilyLocked(tf);
+
+            // record the default family if this is it
+            if (names == gDefaultNames) {
+                gDefaultFamily = family;
+            }
+            // add the names to map to this family
+            while (*names) {
+                addNameLocked(*names, family);
+                names += 1;
+            }
+        }
+    }
+    finaliseFallbackFontListsLocked();
+
+    // do this after all fonts are loaded. This is our default font, and it
+    // acts as a sentinel so we only execute loadSystemFontsLocked() once
+    gDefaultNormal = findBestFaceLocked(gDefaultFamily, SkTypeface::kNormal);
+
+    SkDEBUGCODE(dumpGlobalsLocked());
+}
+
+static int findFallbackFontIndex(SkFontID fontId, FallbackFontList* currentFallbackList) {
+    for (int i = 0; i < currentFallbackList->fList.count(); i++) {
+        if (currentFallbackList->fList[i].fFontID == fontId) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static void loadSystemFontsLocked() {
+    if (!gDefaultNormal) {
+        initSystemFontsLocked();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    // lookup and record if the font is custom (i.e. not a system font)
+    bool isCustomFont = !((FamilyTypeface*)face)->isSysFont();
+    stream->writeBool(isCustomFont);
+
+    if (isCustomFont) {
+        SkStream* fontStream = ((FamilyTypeface*)face)->openStream();
+
+        // store the length of the custom font
+        uint32_t len = fontStream->getLength();
+        stream->write32(len);
+
+        // store the entire font in the serialized stream
+        void* fontData = malloc(len);
+
+        fontStream->read(fontData, len);
+        stream->write(fontData, len);
+
+        fontStream->unref();
+        free(fontData);
+//      SkDebugf("--- fonthost custom serialize %d %d\n", face->style(), len);
+
+    } else {
+        const char* name = ((FamilyTypeface*)face)->getUniqueString();
+
+        stream->write8((uint8_t)face->style());
+
+        if (NULL == name || 0 == *name) {
+            stream->writePackedUInt(0);
+//          SkDebugf("--- fonthost serialize null\n");
+        } else {
+            uint32_t len = strlen(name);
+            stream->writePackedUInt(len);
+            stream->write(name, len);
+//          SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
+        }
+    }
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+    return deserializeLocked(stream);
+}
+
+static SkTypeface* deserializeLocked(SkStream* stream) {
+    loadSystemFontsLocked();
+
+    // check if the font is a custom or system font
+    bool isCustomFont = stream->readBool();
+
+    if (isCustomFont) {
+
+        // read the length of the custom font from the stream
+        uint32_t len = stream->readU32();
+
+        // generate a new stream to store the custom typeface
+        SkMemoryStream* fontStream = new SkMemoryStream(len);
+        stream->read((void*)fontStream->getMemoryBase(), len);
+
+        SkTypeface* face = createTypefaceFromStreamLocked(fontStream);
+
+        fontStream->unref();
+
+//      SkDebugf("--- fonthost custom deserialize %d %d\n", face->style(), len);
+        return face;
+
+    } else {
+        int style = stream->readU8();
+
+        int len = stream->readPackedUInt();
+        if (len > 0) {
+            SkString str;
+            str.resize(len);
+            stream->read(str.writable_str(), len);
+
+            for (int i = 0; i < gSystemFonts.count(); i++) {
+                if (strcmp(gSystemFonts[i].fFileName, str.c_str()) == 0) {
+                    // backup until we hit the fNames
+                    for (int j = i; j >= 0; --j) {
+                        if (gSystemFonts[j].fNames != NULL) {
+                            return createTypefaceLocked(NULL,
+                                    gSystemFonts[j].fNames[0], NULL, 0,
+                                    (SkTypeface::Style)style);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
+static SkTypeface* createTypefaceLocked(const SkTypeface* familyFace,
+        const char familyName[], const void* data, size_t bytelength,
+        SkTypeface::Style style) {
+    loadSystemFontsLocked();
+
+    // clip to legal style bits
+    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+
+    SkTypeface* tf = NULL;
+
+    if (NULL != familyFace) {
+        tf = findTypefaceLocked(familyFace, style);
+    } else if (NULL != familyName) {
+//        SkDebugf("======= familyName <%s>\n", familyName);
+        tf = findTypefaceLocked(familyName, style);
+    }
+
+    if (NULL == tf) {
+        tf = findBestFaceLocked(gDefaultFamily, style);
+    }
+
+    // we ref(), since the semantic is to return a new instance
+    tf->ref();
+    return tf;
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t fontID) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+    return openStreamLocked(fontID);
+}
+
+static SkStream* openStreamLocked(uint32_t fontID) {
+    FamilyTypeface* tf = (FamilyTypeface*)findFromUniqueIDLocked(fontID);
+    SkStream* stream = tf ? tf->openStream() : NULL;
+
+    if (stream && stream->getLength() == 0) {
+        stream->unref();
+        stream = NULL;
+    }
+    return stream;
+}
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
+                               int32_t* index) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+    return getFileNameLocked(fontID, path, length, index);
+}
+
+static size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int32_t* index) {
+    FamilyTypeface* tf = (FamilyTypeface*)findFromUniqueIDLocked(fontID);
+    const char* src = tf ? tf->getFilePath() : NULL;
+
+    if (src) {
+        size_t size = strlen(src);
+        if (path) {
+            memcpy(path, src, SkMin32(size, length));
+        }
+        if (index) {
+            *index = 0; // we don't have collections (yet)
+        }
+        return size;
+    } else {
+        return 0;
+    }
+}
+
+SkFontID SkFontHost::NextLogicalFont(const SkScalerContext::Rec& rec) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+    return nextLogicalFontLocked(rec);
+}
+
+static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) {
+    loadSystemFontsLocked();
+
+    const SkTypeface* origTypeface = findFromUniqueIDLocked(rec.fOrigFontID);
+    const SkTypeface* currTypeface = findFromUniqueIDLocked(rec.fFontID);
+
+    FallbackFontList* currentFallbackList =
+            getFallbackFontListLocked(rec.fLanguage);
+    SkASSERT(currentFallbackList);
+
+    SkASSERT(origTypeface != 0);
+    SkASSERT(currTypeface != 0);
+
+    // Our fallback list always stores the id of the plain in each fallback
+    // family, so we transform currFontID to its plain equivalent.
+    SkFontID plainFontID = findTypefaceLocked(currTypeface, SkTypeface::kNormal)->uniqueID();
+
+    /*  First see if fontID is already one of our fallbacks. If so, return
+        its successor. If fontID is not in our list, then return the first one
+        in our list. Note: list is zero-terminated, and returning zero means
+        we have no more fonts to use for fallbacks.
+     */
+    int plainFallbackFontIndex = findFallbackFontIndex(plainFontID, currentFallbackList);
+    int nextFallbackFontIndex = plainFallbackFontIndex + 1;
+
+    // If a rec object is set to prefer "kDefault_Variant" it means they have no preference
+    // In this case, we set the value to "kCompact_Variant"
+    SkPaint::FontVariant recPreference = rec.fFontVariant;
+    if (recPreference == SkPaint::kDefault_Variant) {
+        recPreference = SkPaint::kCompact_Variant;
+    }
+    SkFontID nextFontID = 0;
+    while (nextFallbackFontIndex < currentFallbackList->fList.count()) {
+        bool normalFont =
+                (currentFallbackList->fList[nextFallbackFontIndex].fVariant == SkPaint::kDefault_Variant);
+        bool fontChosen = (currentFallbackList->fList[nextFallbackFontIndex].fVariant == recPreference);
+        if (normalFont || fontChosen) {
+            const SkTypeface* nextTypeface =
+                    findFromUniqueIDLocked(currentFallbackList->fList[nextFallbackFontIndex].fFontID);
+            nextFontID = findTypefaceLocked(nextTypeface, origTypeface->style())->uniqueID();
+            break;
+        }
+        nextFallbackFontIndex++;
+    }
+
+    SkDEBUGF(("---- nextLogicalFont: currFontID=%d, origFontID=%d, plainFontID=%d, "
+            "plainFallbackFontIndex=%d, nextFallbackFontIndex=%d "
+            "=> nextFontID=%d", rec.fFontID, rec.fOrigFontID, plainFontID,
+            plainFallbackFontIndex, nextFallbackFontIndex, nextFontID));
+    return nextFontID;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+    return createTypefaceFromStreamLocked(stream);
+}
+
+static SkTypeface* createTypefaceFromStreamLocked(SkStream* stream) {
+    if (NULL == stream || stream->getLength() <= 0) {
+        return NULL;
+    }
+
+    // Make sure system fonts are loaded first to comply with the assumption
+    // that the font's uniqueID can be found using the findUniqueIDLocked method.
+    loadSystemFontsLocked();
+
+    bool isFixedWidth;
+    SkTypeface::Style style;
+
+    if (find_name_and_attributes(stream, NULL, &style, &isFixedWidth)) {
+        SkTypeface* typeface = SkNEW_ARGS(StreamTypeface, (style, false, stream, isFixedWidth));
+        addTypefaceLocked(typeface, NULL);
+        return typeface;
+    } else {
+        return NULL;
+    }
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
+    SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
+    // since we created the stream, we let go of our ref() here
+    stream->unref();
+    return face;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Function from SkTypeface_android.h
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style,
+        SkPaint::FontVariant fontVariant) {
+    SkTypeface* face = FindBestFace(gDefaultFamily, style);
+    if (!face) {
+        return 0;
+    }
+
+    SkPaint paint;
+    paint.setTypeface(face);
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    paint.setFontVariant(fontVariant);
+
+    SkAutoGlyphCache autoCache(paint, NULL);
+    SkGlyphCache*    cache = autoCache.getCache();
+    SkFontID         fontID = 0;
+
+    SkScalerContext* ctx = cache->getScalerContext();
+    if (ctx) {
+        return ctx->findTypefaceIdForChar(uni);
+    }
+    return 0;
+}
+
+struct HB_UnicodeMapping {
+    HB_Script script;
+    const SkUnichar unicode;
+};
+
+/*
+ * The following scripts are not complex fonts and we do not expect them to be parsed by this table
+ * HB_Script_Common,
+ * HB_Script_Greek,
+ * HB_Script_Cyrillic,
+ * HB_Script_Hangul
+ * HB_Script_Inherited
+ */
+
+static HB_UnicodeMapping HB_UnicodeMappingArray[] {
+    {HB_Script_Armenian,      0x0531},
+    {HB_Script_Hebrew,        0x0591},
+    {HB_Script_Arabic,        0x0600},
+    {HB_Script_Syriac,        0x0710},
+    {HB_Script_Thaana,        0x0780},
+    {HB_Script_Nko,           0x07C0},
+    {HB_Script_Devanagari,    0x0901},
+    {HB_Script_Bengali,       0x0981},
+    {HB_Script_Gurmukhi,      0x0A10},
+    {HB_Script_Gujarati,      0x0A90},
+    {HB_Script_Oriya,         0x0B10},
+    {HB_Script_Tamil,         0x0B82},
+    {HB_Script_Telugu,        0x0C10},
+    {HB_Script_Kannada,       0x0C90},
+    {HB_Script_Malayalam,     0x0D10},
+    {HB_Script_Sinhala,       0x0D90},
+    {HB_Script_Thai,          0x0E01},
+    {HB_Script_Lao,           0x0E81},
+    {HB_Script_Tibetan,       0x0F00},
+    {HB_Script_Myanmar,       0x1000},
+    {HB_Script_Georgian,      0x10A0},
+    // we don't currently support HB_Script_Ethiopic, it is a placeholder for an upstream merge
+    //{HB_Script_Ethiopic,    0x1200},
+    {HB_Script_Ogham,         0x1680},
+    {HB_Script_Runic,         0x16A0},
+    {HB_Script_Khmer,         0x1780},
+};
+
+// returns 0 for "Not Found"
+static SkUnichar getUnicodeFromHBScript(HB_Script script) {
+    SkUnichar unichar = 0;
+    int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
+    for (int i = 0; i < numSupportedFonts; i++) {
+        if (script == HB_UnicodeMappingArray[i].script) {
+            unichar = HB_UnicodeMappingArray[i].unicode;
+            break;
+        }
+    }
+    return unichar;
+}
+
+struct TypefaceLookupStruct {
+    HB_Script            script;
+    SkTypeface::Style    style;
+    SkPaint::FontVariant fontVariant;
+    SkTypeface*          typeface;
+};
+
+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;
+    }
+    if (first.style != second.style) {
+        return (first.style > second.style) ? 1 : -1;
+    }
+    if (first.fontVariant != second.fontVariant) {
+        return (first.fontVariant > second.fontVariant) ? 1 : -1;
+    }
+    return 0;
+}
+
+SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
+        SkPaint::FontVariant fontVariant) {
+    SkTypeface* retTypeface = NULL;
+
+    SkAutoMutexAcquire ac(gTypefaceTableMutex); // Note: NOT gFamilyHeadAndNameListMutex
+    TypefaceLookupStruct key;
+    key.script = script;
+    key.style = style;
+    key.fontVariant = fontVariant;
+    int index = SkTSearch<TypefaceLookupStruct>(
+            (const TypefaceLookupStruct*) gTypefaceTable.begin(),
+            gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct),
+            &typefaceLookupCompare);
+    if (index >= 0) {
+        retTypeface = gTypefaceTable[index].typeface;
+    }
+    else {
+        SkUnichar unichar = getUnicodeFromHBScript(script);
+        if (!unichar) {
+            return NULL;
+        }
+        SkFontID newFontID = findFontIDForChar(unichar, style, fontVariant);
+        // retrieve the typeface that corresponds to this fontID
+        retTypeface = FindFromUniqueID(newFontID);
+        key.typeface = retTypeface;
+        index = ~index;
+        *gTypefaceTable.insert(index) = key;
+    }
+    // we ref(), the caller is expected to unref when they are done
+    SkSafeRef(retTypeface);
+    return retTypeface;
+}
diff --git a/legacy/src/ports/SkFontHost_ascender.cpp b/legacy/src/ports/SkFontHost_ascender.cpp
new file mode 100644
index 0000000..ccfedef
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_ascender.cpp
@@ -0,0 +1,230 @@
+
+/*
+ * 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 "SkScalerContext.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDescriptor.h"
+#include "SkFDot6.h"
+#include "SkFontHost.h"
+#include "SkMask.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkThread.h"
+#include "SkTemplates.h"
+
+#include <acaapi.h>
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkMMapStream.h"
+
+class SkScalerContext_Ascender : public SkScalerContext {
+public:
+    SkScalerContext_Ascender(const SkDescriptor* desc);
+    virtual ~SkScalerContext_Ascender();
+
+protected:
+    virtual unsigned generateGlyphCount();
+    virtual uint16_t generateCharToGlyph(SkUnichar uni);
+    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);
+
+private:
+    aca_FontHandle  fHandle;
+    void*   fWorkspace;
+    void*   fGlyphWorkspace;
+    SkStream*   fFontStream;
+    SkStream*   fHintStream;
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+SkScalerContext_Ascender::SkScalerContext_Ascender(const SkDescriptor* desc)
+    : SkScalerContext(desc)
+{
+    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;
+
+    fRec.getSingleMatrix(&m);
+
+    //  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;
+    rec.doAdjust = true;
+    rec.doExceptions = true;
+    rec.doGlyphHints = true;
+    rec.doInterpolate = true;
+    rec.grayMode = 2;
+    aca_Set_Font_Attributes(fHandle, &rec, &size);
+    
+    fGlyphWorkspace = sk_malloc_throw(size);
+    aca_Set_Glyph_Memory(fHandle, fGlyphWorkspace);
+}
+
+SkScalerContext_Ascender::~SkScalerContext_Ascender()
+{
+    delete fHintStream;
+    delete fFontStream;
+    sk_free(fGlyphWorkspace);
+    sk_free(fWorkspace);
+    sk_free(fHandle);
+}
+
+unsigned SkScalerContext_Ascender::generateGlyphCount()
+{
+    return 1000;
+}
+
+uint16_t SkScalerContext_Ascender::generateCharToGlyph(SkUnichar uni)
+{
+    return (uint16_t)(uni & 0xFFFF);
+}
+
+void SkScalerContext_Ascender::generateMetrics(SkGlyph* glyph)
+{
+    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;
+
+    aca_Rasterize(glyph->getGlyphID(), fHandle, &rec, &topLeft);
+
+    if (false)  // error
+    {
+ERROR:
+        glyph->fWidth   = 0;
+        glyph->fHeight  = 0;
+        glyph->fTop     = 0;
+        glyph->fLeft    = 0;
+        glyph->fAdvanceX = 0;
+        glyph->fAdvanceY = 0;
+        return;
+    }
+    
+    glyph->fWidth = rec.width;
+    glyph->fHeight = rec.rows;
+    glyph->fRowBytes = rec.width;
+    glyph->fTop = -topLeft.y;
+    glyph->fLeft = topLeft.x;
+    glyph->fAdvanceX = SkIntToFixed(adv);
+    glyph->fAdvanceY = SkIntToFixed(0);
+}
+
+void SkScalerContext_Ascender::generateImage(const SkGlyph& glyph)
+{
+    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)
+    {
+        memcpy(dst, src, glyph.fWidth);
+        src += rec.pitch;
+        dst += glyph.fRowBytes;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void SkScalerContext_Ascender::generatePath(const SkGlyph& glyph, SkPath* path)
+{
+    SkRect r;
+    
+    r.set(0, 0, SkIntToScalar(4), SkIntToScalar(4));
+    path->reset();
+    path->addRect(r);
+}
+
+void SkScalerContext_Ascender::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
+{
+    if (NULL == mx && NULL == my)
+        return;
+
+    if (mx)
+    {
+        mx->fTop = SkIntToScalar(-16);
+        mx->fAscent = SkIntToScalar(-16);
+        mx->fDescent = SkIntToScalar(4);
+        mx->fBottom = SkIntToScalar(4);
+        mx->fLeading = 0;
+
+        // FIXME:
+        mx->fAvgCharWidth = 0;
+        mx->fXMin = 0;
+        mx->fXMax = 0;
+        mx->fXHeight = 0;
+    }
+    if (my)
+    {
+        my->fTop = SkIntToScalar(-16);
+        my->fAscent = SkIntToScalar(-16);
+        my->fDescent = SkIntToScalar(4);
+        my->fBottom = SkIntToScalar(4);
+        my->fLeading = 0;
+
+        // FIXME:
+        my->fAvgCharWidth = 0;
+        my->fXMin = 0;
+        my->fXMax = 0;
+        my->fXHeight = 0;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)
+{
+    return SkNEW_ARGS(SkScalerContext_Ascender, (desc));
+}
+
diff --git a/legacy/src/ports/SkFontHost_fontconfig.cpp b/legacy/src/ports/SkFontHost_fontconfig.cpp
new file mode 100644
index 0000000..775a7c7
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_fontconfig.cpp
@@ -0,0 +1,354 @@
+
+/*
+ * 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 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>
+
+#include <fontconfig/fontconfig.h>
+
+#include "SkFontHost.h"
+#include "SkStream.h"
+
+// This is 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.
+// -----------------------------------------------------------------------------
+SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
+static std::map<std::string, unsigned> global_fc_map;
+static std::map<unsigned, std::string> global_fc_map_inverted;
+static std::map<uint32_t, SkTypeface *> global_fc_typefaces;
+static unsigned global_fc_map_next_id = 0;
+
+static unsigned UniqueIdToFileId(unsigned uniqueid)
+{
+    return uniqueid >> 8;
+}
+
+static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid)
+{
+    return static_cast<SkTypeface::Style>(uniqueid & 0xff);
+}
+
+static unsigned FileIdAndStyleToUniqueId(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)
+    { }
+};
+
+// -----------------------------------------------------------------------------
+// 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;
+        fcvalue.type = vtype;
+        switch (vtype) {
+            case FcTypeString:
+                fcvalue.u.s = (FcChar8*) value;
+                break;
+            case FcTypeInteger:
+                fcvalue.u.i = (int)(intptr_t)value;
+                break;
+            default:
+                SkDEBUGFAIL("FontMatch unhandled type");
+        }
+        FcPatternAdd(pattern, type, fcvalue, 0);
+
+        if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0)
+            family_requested = (const char*) value;
+
+        type = va_arg(ap, const char *);
+        if (!type)
+            break;
+        // FcType is promoted to int when passed through ...
+        vtype = static_cast<FcType>(va_arg(ap, int));
+        value = va_arg(ap, const void *);
+    };
+    va_end(ap);
+
+    FcConfigSubstitute(0, 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;
+
+    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;
+    FcPattern* face_match = NULL;
+
+    {
+        SkAutoMutexAcquire ac(global_fc_map_lock);
+        FcInit();
+    }
+
+    if (familyFace) {
+        // Here we use the inverted global id map to find the filename from the
+        // SkTypeface object. Given the filename we can ask fontconfig for the
+        // 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())
+            return NULL;
+
+        FcInit();
+        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);
+            return NULL;
+        }
+        // At this point, @family is pointing into the @face_match object so we
+        // cannot release it yet.
+
+        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;
+    FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
+                                 FC_WEIGHT, FcTypeInteger, bold,
+                                 FC_SLANT, FcTypeInteger, italic,
+                                 NULL);
+    if (face_match)
+        FcPatternDestroy(face_match);
+
+    if (!match)
+        return NULL;
+
+    FcChar8* filename;
+    if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) {
+        FcPatternDestroy(match);
+        return NULL;
+    }
+    // Now @filename is pointing into @match
+
+    const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
+    const unsigned id = FileIdAndStyleToUniqueId(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)
+{
+    SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
+    return NULL;
+}
+
+// static
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
+{
+    SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
+    return NULL;
+}
+
+// static
+SkStream* SkFontHost::OpenStream(uint32_t id)
+{
+    SkAutoMutexAcquire ac(global_fc_map_lock);
+    const unsigned fileid = UniqueIdToFileId(id);
+
+    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) {
+    SkAutoMutexAcquire ac(global_fc_map_lock);
+    const unsigned fileid = UniqueIdToFileId(fontID);
+
+    std::map<unsigned, std::string>::const_iterator i =
+    global_fc_map_inverted.find(fileid);
+    if (i == global_fc_map_inverted.end()) {
+        return 0;
+    }
+
+    const std::string& str = i->second;
+    if (path) {
+        memcpy(path, str.c_str(), SkMin32(str.size(), length));
+    }
+    if (index) {    // TODO: check if we're in a TTC
+        *index = 0;
+    }
+    return str.size();
+}
+
+void SkFontHost::Serialize(const SkTypeface*, SkWStream*) {
+    SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    // We don't handle font fallback, WebKit does.
+    return 0;
+}
+
diff --git a/legacy/src/ports/SkFontHost_freetype_mac.cpp b/legacy/src/ports/SkFontHost_freetype_mac.cpp
new file mode 100644
index 0000000..e51f802
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_freetype_mac.cpp
@@ -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.
+ */
+
+#include "SkFontHost.h"
+#include "SkMMapStream.h"
+#include "SkTypefaceCache.h"
+
+#define FONT_PATH   "/Library/Fonts/Skia.ttf"
+
+class FTMacTypeface : public SkTypeface {
+public:
+    FTMacTypeface(Style style, uint32_t id, SkStream* stream) : SkTypeface(style, id) {
+        // we take ownership of the stream
+        fStream = stream;
+    }
+
+    virtual ~FTMacTypeface() {
+        fStream->unref();
+    }
+
+    SkStream* fStream;    
+};
+
+static FTMacTypeface* create_from_path(const char path[]) {
+    SkStream* stream = new SkMMAPStream(path);
+    size_t size = stream->getLength();
+    SkASSERT(size);
+    FTMacTypeface* tf = new FTMacTypeface(SkTypeface::kNormal,
+                                          SkTypefaceCache::NewFontID(),
+                                          stream);
+    SkTypefaceCache::Add(tf, SkTypeface::kNormal);
+    return tf;
+}
+
+static SkTypeface* ref_default_typeface() {
+    static SkTypeface* gDef;
+
+    if (NULL == gDef) {
+        gDef = create_from_path(FONT_PATH);
+    }
+    
+    gDef->ref();
+    return gDef;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                       const char familyName[],
+                                       const void* data, size_t bytelength,
+                                       SkTypeface::Style style) {
+    return ref_default_typeface();
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    return create_from_path(path);
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t fontID) {
+    FTMacTypeface* tf = (FTMacTypeface*)SkTypefaceCache::FindByID(fontID);
+    if (tf) {
+        tf->fStream->ref();
+        return tf->fStream;
+    }
+    return NULL;
+}
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
+                               int32_t* index) {
+    if (path) {
+        strncpy(path, "font", length);
+    }
+    if (index) {
+        *index = 0;
+    }
+    return 4;
+}
+
+void SkFontHost::Serialize(const SkTypeface*, SkWStream*) {
+    SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    return 0;
+}
+
+#include "SkTypeface_mac.h"
+
+SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
+    SkDEBUGFAIL("Not supported");
+    return NULL;
+}
+
diff --git a/src/ports/SkFontHost_gamma.cpp b/legacy/src/ports/SkFontHost_gamma.cpp
similarity index 100%
rename from src/ports/SkFontHost_gamma.cpp
rename to legacy/src/ports/SkFontHost_gamma.cpp
diff --git a/src/ports/SkFontHost_gamma_none.cpp b/legacy/src/ports/SkFontHost_gamma_none.cpp
similarity index 100%
rename from src/ports/SkFontHost_gamma_none.cpp
rename to legacy/src/ports/SkFontHost_gamma_none.cpp
diff --git a/legacy/src/ports/SkFontHost_linux.cpp b/legacy/src/ports/SkFontHost_linux.cpp
new file mode 100644
index 0000000..be99576
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_linux.cpp
@@ -0,0 +1,594 @@
+
+/*
+ * 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 "SkDescriptor.h"
+#include "SkMMapStream.h"
+#include "SkOSFile.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#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/"
+#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;
+
+/*  This guy holds a mapping of a name -> family, used for looking up fonts.
+ Since it is stored in a stretchy array that doesn't preserve object
+ semantics, we don't use constructor/destructors, but just have explicit
+ helpers to manage our internal bookkeeping.
+ */
+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);
+        fFamily = family;   // we don't own this, so just record the referene
+    }
+    void destruct()
+    {
+        free((char*)fName);
+        // we don't own family, so just ignore our reference
+    }
+};
+
+// we use atomic_inc to grow this for each typeface we create
+static int32_t gUniqueFontID;
+
+// this is the mutex that protects these globals
+SK_DECLARE_STATIC_MUTEX(gFamilyMutex);
+static FamilyRec* gFamilyHead;
+static SkTDArray<NameFamilyPair> gNameList;
+
+struct FamilyRec {
+    FamilyRec*  fNext;
+    SkTypeface* fFaces[4];
+    
+    FamilyRec()
+    {
+        fNext = gFamilyHead;
+        memset(fFaces, 0, sizeof(fFaces));
+        gFamilyHead = this;
+    }
+};
+
+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];
+    }
+    // look for a matching bold
+    style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
+    if (faces[style] != NULL) {
+        return faces[style];
+    }
+    // look for the plain
+    if (faces[SkTypeface::kNormal] != NULL) {
+        return faces[SkTypeface::kNormal];
+    }
+    // look for anything
+    for (int i = 0; i < 4; i++) {
+        if (faces[i] != NULL) {
+            return faces[i];
+        }
+    }
+    // should never get here, since the faces list should not be empty
+    SkDEBUGFAIL("faces list is empty");
+    return NULL;
+}
+
+static FamilyRec* find_family(const SkTypeface* member) {
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            if (curr->fFaces[i] == member) {
+                return curr;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            SkTypeface* face = curr->fFaces[i];
+            if (face != NULL && face->uniqueID() == uniqueID) {
+                return face;
+            }
+        }
+        curr = curr->fNext;
+    }
+    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
+ */
+static FamilyRec* remove_from_family(const SkTypeface* face) {
+    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;
+        }
+    }
+    return family;  // return the empty family
+}
+
+// maybe we should make FamilyRec be doubly-linked
+static void detach_and_delete_family(FamilyRec* family) {
+    FamilyRec* curr = gFamilyHead;
+    FamilyRec* prev = NULL;
+    
+    while (curr != NULL) {
+        FamilyRec* next = curr->fNext;
+        if (curr == family) {
+            if (prev == NULL) {
+                gFamilyHead = next;
+            } else {
+                prev->fNext = next;
+            }
+            SkDELETE(family);
+            return;
+        }
+        prev = curr;
+        curr = next;
+    }
+    SkDEBUGFAIL("Yikes, couldn't find family in our list to remove/delete");
+}
+
+static FamilyRec* find_familyrec(const char name[]) {
+    const NameFamilyPair* list = gNameList.begin();    
+    int index = SkStrLCSearch(&list[0].fName, gNameList.count(), name,
+                              sizeof(list[0]));
+    return index >= 0 ? list[index].fFamily : NULL;
+}
+
+static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
+    FamilyRec* rec = find_familyrec(name);
+    return rec ? find_best_face(rec, style) : NULL;
+}
+
+static SkTypeface* find_typeface(const SkTypeface* familyMember,
+                                 SkTypeface::Style style) {
+    const FamilyRec* family = find_family(familyMember);
+    return family ? find_best_face(family, style) : NULL;
+}
+
+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);
+    }
+}
+
+static void remove_from_names(FamilyRec* emptyFamily) {
+#ifdef SK_DEBUG
+    for (int i = 0; i < 4; i++) {
+        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];
+        if (pair->fFamily == emptyFamily) {
+            pair->destruct();
+            list.remove(i);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FamilyTypeface : public SkTypeface {
+public:
+    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);
+        if (NULL != family) {
+            remove_from_names(family);
+            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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* This subclass is just a place holder for when we have no fonts available.
+    It exists so that our globals (e.g. gFamilyHead) that expect *something*
+    will not be null.
+ */
+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;
+};
+
+class StreamTypeface : public FamilyTypeface {
+public:
+    StreamTypeface(Style style, bool sysFont, FamilyRec* family,
+                   SkStream* stream, bool isFixedWidth)
+    : INHERITED(style, sysFont, family, isFixedWidth) {
+        stream->ref();
+        fStream = stream;
+    }
+    virtual ~StreamTypeface() {
+        fStream->unref();
+    }
+    
+    // overrides
+    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;
+};
+
+class FileTypeface : public FamilyTypeface {
+public:
+    FileTypeface(Style style, bool sysFont, FamilyRec* family,
+                 const char path[], bool isFixedWidth)
+        : 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);
+            // maybe MMAP isn't supported. try FILE
+            stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
+            if (stream->getLength() <= 0) {
+                SkDELETE(stream);
+                stream = NULL;
+            }
+        }
+        return stream;
+    }
+
+    virtual const char* getUniqueString() const {
+        const char* str = strrchr(fPath.c_str(), '/');
+        if (str) {
+            str += 1;   // skip the '/'
+        }
+        return str;
+    }
+    
+private:
+    SkString fPath;
+    
+    typedef FamilyTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool get_name_and_style(const char path[], SkString* name,
+                               SkTypeface::Style* style, bool* isFixedWidth) {    
+    SkMMAPStream stream(path);
+    if (stream.getLength() > 0) {
+        return find_name_and_attributes(&stream, name, style, isFixedWidth);
+    }
+    else {
+        SkFILEStream stream(path);
+        if (stream.getLength() > 0) {
+            return find_name_and_attributes(&stream, name, style, isFixedWidth);
+        }
+    }
+    
+    SkDebugf("---- failed to open <%s> as a font\n", path);
+    return false;
+}
+
+// these globals are assigned (once) by load_system_fonts()
+static SkTypeface* gFallBackTypeface;
+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");
+    SkString        name;
+    int             count = 0;
+
+    while (iter.next(&name, false)) {
+        SkString filename;
+        GetFullPathForSysFonts(&filename, name.c_str());
+
+        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;
+        }
+
+        // this constructor puts us into the global gFamilyHead llist
+        FamilyTypeface* tf = SkNEW_ARGS(FileTypeface,
+                                        (style,
+                                         true,  // system-font (cannot delete)
+                                         family, // what family to join
+                                         filename.c_str(),
+                                         isFixedWidth) // filename
+                                        );
+
+        if (NULL == family) {
+            add_name(realname.c_str(), tf->getFamily());
+        }
+        count += 1;
+    }
+
+    if (0 == count) {
+        SkNEW(EmptyTypeface);
+    }
+
+    // do this after all fonts are loaded. This is our default font, and it
+    // acts as a sentinel so we only execute load_system_fonts() once
+    static const char* gDefaultNames[] = {
+        "Arial", "Verdana", "Times New Roman", NULL
+    };
+    const char** names = gDefaultNames;
+    while (*names) {
+        SkTypeface* tf = find_typeface(*names++, SkTypeface::kNormal);
+        if (tf) {
+            gDefaultNormal = tf;
+            break;
+        }
+    }
+    // check if we found *something*
+    if (NULL == gDefaultNormal) {
+        if (NULL == gFamilyHead) {
+            sk_throw();
+        }
+        for (int i = 0; i < 4; i++) {
+            if ((gDefaultNormal = gFamilyHead->fFaces[i]) != NULL) {
+                break;
+            }
+        }
+    }
+    if (NULL == gDefaultNormal) {
+        sk_throw();
+    }
+    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");
+    } else {
+        uint32_t len = strlen(name);
+        stream->writePackedUInt(len);
+        stream->write(name, len);
+        //      SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle());
+    }
+#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);
+                    }
+                }
+            }
+        }
+    }
+    return SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, (SkTypeface::Style)style);
+#endif
+    sk_throw();
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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); 
+    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;
+    }
+    return stream;
+}
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
+                               int32_t* index) {
+    SkDebugf("SkFontHost::GetFileName unimplemented\n");
+    return 0;
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    if (NULL == stream || stream->getLength() <= 0) {
+        SkDELETE(stream);
+        return NULL;
+    }
+
+    bool isFixedWidth;
+    SkTypeface::Style style;
+    if (find_name_and_attributes(stream, NULL, &style, &isFixedWidth)) {
+        return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth));
+    } else {
+        return NULL;
+    }
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    SkTypeface* face = NULL;
+    SkFILEStream* stream = SkNEW_ARGS(SkFILEStream, (path));
+
+    if (stream->isValid()) {
+        face = CreateTypefaceFromStream(stream);
+    }
+    stream->unref();
+    return face;
+}
+
diff --git a/legacy/src/ports/SkFontHost_mac.cpp b/legacy/src/ports/SkFontHost_mac.cpp
new file mode 100755
index 0000000..7d12f53
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_mac.cpp
@@ -0,0 +1,39 @@
+
+/*
+ * 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.
+ */
+
+
+
+/*
+ ** Mac Text API
+ **
+ **
+ ** Two text APIs are available on the Mac, ATSUI and CoreText.
+ **
+ ** ATSUI is available on all versions of Mac OS X, but is 32-bit only.
+ **
+ ** The replacement API, CoreText, supports both 32-bit and 64-bit builds
+ ** but is only available from Mac OS X 10.5 onwards.
+ **
+ ** To maintain support for Mac OS X 10.4, we default to ATSUI in 32-bit
+ ** builds unless SK_USE_CORETEXT is defined.
+*/
+#ifndef SK_USE_CORETEXT
+    #if TARGET_RT_64_BIT || defined(SK_USE_MAC_CORE_TEXT)
+        #define SK_USE_CORETEXT                                     1
+    #else
+        #define SK_USE_CORETEXT                                     0
+    #endif
+#endif
+
+#if SK_USE_CORETEXT
+    #include "SkFontHost_mac_coretext.cpp"
+#else
+    #include "SkFontHost_mac_atsui.cpp"
+#endif
+
+
diff --git a/legacy/src/ports/SkFontHost_mac_atsui.cpp b/legacy/src/ports/SkFontHost_mac_atsui.cpp
new file mode 100644
index 0000000..ae32036
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_mac_atsui.cpp
@@ -0,0 +1,594 @@
+
+/*
+ * 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 <Carbon/Carbon.h>
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkEndian.h"
+#include "SkFloatingPoint.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+
+const char* gDefaultfont = "Arial"; // hard code for now
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
+
+static inline SkPoint F32PtToSkPoint(const Float32Point p) {
+    SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
+    return sp;
+}
+
+static inline uint32_t _rotl(uint32_t v, uint32_t r) {
+    return (v << r | v >> (32 - r));
+}
+
+class SkTypeface_Mac : public SkTypeface {
+public:
+    SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
+        : SkTypeface(style, id) {}
+};
+
+#pragma mark -
+
+static uint32_t find_from_name(const char name[]) {
+    CFStringRef str = CFStringCreateWithCString(NULL, name,
+                                                kCFStringEncodingUTF8);
+    uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
+    CFRelease(str);
+    return fontID;
+}
+
+static uint32_t find_default_fontID() {
+    static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
+
+    uint32_t fontID;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
+        fontID = find_from_name(gDefaultNames[i]);
+        if (fontID) {
+            return fontID;
+        }
+    }
+    sk_throw();
+    return 0;
+}
+
+static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
+    uint32_t fontID = 0;
+    if (NULL != name) {
+        fontID = find_from_name(name);
+    }
+    if (0 == fontID) {
+        fontID = find_default_fontID();
+    }
+    // we lie (for now) and report that we found the exact style bits
+    return new SkTypeface_Mac(style, fontID);
+}
+
+#pragma mark -
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+    SkScalerContext_Mac(const SkDescriptor* desc);
+    virtual ~SkScalerContext_Mac();
+
+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);
+
+private:
+    ATSUTextLayout  fLayout;
+    ATSUStyle       fStyle;
+    CGColorSpaceRef fGrayColorSpace;
+    CGAffineTransform   fTransform;
+
+    static OSStatus MoveTo(const Float32Point *pt, void *cb);
+    static OSStatus Line(const Float32Point *pt, void *cb);
+    static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
+    static OSStatus Close(void *cb);
+};
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+    // 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);
+
+    // we don't support LCD text
+    if (SkMask::kLCD16_Format == rec->fMaskFormat ||
+        SkMask::kLCD32_Format == rec->fMaskFormat) {
+        rec->fMaskFormat = SkMask::kA8_Format;
+    }
+}
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+    : SkScalerContext(desc), fLayout(0), fStyle(0)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    OSStatus err;
+
+    err = ::ATSUCreateStyle(&fStyle);
+    SkASSERT(0 == err);
+
+    SkMatrix    m;
+    fRec.getSingleMatrix(&m);
+
+    fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
+                                       SkScalarToFloat(m[SkMatrix::kMSkewX]),
+                                       SkScalarToFloat(m[SkMatrix::kMSkewY]),
+                                       SkScalarToFloat(m[SkMatrix::kMScaleY]),
+                                       SkScalarToFloat(m[SkMatrix::kMTransX]),
+                                       SkScalarToFloat(m[SkMatrix::kMTransY]));
+
+    ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing;
+    switch (fRec.getHinting()) {
+        case SkPaint::kNo_Hinting:
+        case SkPaint::kSlight_Hinting:
+            renderOpts |= kATSStyleNoHinting;
+            break;
+        case SkPaint::kNormal_Hinting:
+        case SkPaint::kFull_Hinting:
+            renderOpts |= kATSStyleApplyHints;
+            break;
+    }
+
+    ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID);
+    // we put everything in the matrix, so our pt size is just 1.0
+    Fixed fixedSize = SK_Fixed1;
+    static const ATSUAttributeTag tags[] = {
+        kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag
+    };
+    static const ByteCount sizes[] = {
+        sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts)
+    };
+    const ATSUAttributeValuePtr values[] = {
+        &fontID, &fixedSize, &fTransform, &renderOpts
+    };
+    err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
+                              tags, sizes, values);
+    SkASSERT(0 == err);
+
+    err = ::ATSUCreateTextLayout(&fLayout);
+    SkASSERT(0 == err);
+
+    fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac() {
+    ::CGColorSpaceRelease(fGrayColorSpace);
+    ::ATSUDisposeTextLayout(fLayout);
+    ::ATSUDisposeStyle(fStyle);
+}
+
+// man, we need to consider caching this, since it is just dependent on
+// fFontID, and not on any of the other settings like matrix or flags
+unsigned SkScalerContext_Mac::generateGlyphCount() {
+    // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes
+    uint16_t numGlyphs;
+    if (SkFontHost::GetTableData(fRec.fFontID,
+                                 SkSetFourByteTag('m', 'a', 'x', 'p'),
+                                 4, 2, &numGlyphs) != 2) {
+        return 0xFFFF;
+    }
+    return SkEndian_SwapBE16(numGlyphs);
+}
+
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    OSStatus err;
+    UniChar achar = uni;
+    err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
+    err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
+
+    ATSLayoutRecord *layoutPtr;
+    ItemCount count;
+    ATSGlyphRef glyph;
+
+    err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
+    glyph = layoutPtr->glyphID;
+    ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
+    return glyph;
+}
+
+static void set_glyph_metrics_on_error(SkGlyph* glyph) {
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+    glyph->fWidth    = 0;
+    glyph->fHeight   = 0;
+    glyph->fTop      = 0;
+    glyph->fLeft     = 0;
+    glyph->fAdvanceX = 0;
+    glyph->fAdvanceY = 0;
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+    this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
+    GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
+    ATSGlyphScreenMetrics screenMetrics;
+    ATSGlyphIdealMetrics idealMetrics;
+
+    OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
+                                             &screenMetrics);
+    if (noErr != err) {
+        set_glyph_metrics_on_error(glyph);
+        return;
+    }
+    err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics);
+    if (noErr != err) {
+        set_glyph_metrics_on_error(glyph);
+        return;
+    }
+
+    if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) {
+        glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x);
+        glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y);
+    } else {
+        glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x);
+        glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y);
+    }
+
+    // specify an extra 1-pixel border, go tive CG room for its antialiasing
+    // i.e. without this, I was seeing some edges chopped off!
+    glyph->fWidth = screenMetrics.width + 2;
+    glyph->fHeight = screenMetrics.height + 2;
+    glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1;
+    glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1;
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    SkASSERT(fLayout);
+
+    sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
+    CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
+                                              glyph.fWidth, glyph.fHeight, 8,
+                                              glyph.rowBytes(), fGrayColorSpace,
+                                              kCGImageAlphaNone);
+    if (!contextRef) {
+        SkASSERT(false);
+        return;
+    }
+
+    ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
+    ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
+
+    CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount);
+    CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
+    CGContextSetFont(contextRef, fontRef);
+    CGContextSetFontSize(contextRef, 1);
+    CGContextSetTextMatrix(contextRef, fTransform);
+    CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
+                               glyph.fTop + glyph.fHeight, &glyphID, 1);
+
+    ::CGContextRelease(contextRef);
+}
+
+#if 0
+static void convert_metrics(SkPaint::FontMetrics* dst,
+                            const ATSFontMetrics& src) {
+    dst->fTop     = -SkFloatToScalar(src.ascent);
+    dst->fAscent  = -SkFloatToScalar(src.ascent);
+    dst->fDescent = SkFloatToScalar(src.descent);
+    dst->fBottom  = SkFloatToScalar(src.descent);
+    dst->fLeading = SkFloatToScalar(src.leading);
+}
+#endif
+
+static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
+    ByteCount size;
+    OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
+    if (err) {
+        return NULL;
+    }
+    void* data = sk_malloc_throw(size);
+    err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
+    if (err) {
+        sk_free(data);
+        data = NULL;
+    }
+    return data;
+}
+
+static int get_be16(const void* data, size_t offset) {
+    const char* ptr = reinterpret_cast<const char*>(data);
+    uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
+    int n = SkEndian_SwapBE16(value);
+    // now force it to be signed
+    return n << 16 >> 16;
+}
+
+#define SFNT_HEAD_UPEM_OFFSET       18
+#define SFNT_HEAD_YMIN_OFFSET       38
+#define SFNT_HEAD_YMAX_OFFSET       42
+#define SFNT_HEAD_STYLE_OFFSET      44
+
+#define SFNT_HHEA_ASCENT_OFFSET     4
+#define SFNT_HHEA_DESCENT_OFFSET    6
+#define SFNT_HHEA_LEADING_OFFSET    8
+
+static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
+    void* head = get_font_table(font, 'head');
+    if (NULL == head) {
+        return false;
+    }
+    void* hhea = get_font_table(font, 'hhea');
+    if (NULL == hhea) {
+        sk_free(head);
+        return false;
+    }
+
+    int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
+    int ys[5];
+
+    ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
+    ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
+    ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
+    ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
+    ys[4] =  get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
+
+    // now do some cleanup, to ensure y[max,min] are really that
+    if (ys[0] > ys[1]) {
+        ys[0] = ys[1];
+    }
+    if (ys[3] < ys[2]) {
+        ys[3] = ys[2];
+    }
+
+    for (int i = 0; i < 5; i++) {
+        pts[i].set(0, SkIntToScalar(ys[i]) / upem);
+    }
+
+    sk_free(hhea);
+    sk_free(head);
+    return true;
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
+                                              SkPaint::FontMetrics* my) {
+    SkPoint pts[5];
+
+    if (!init_vertical_metrics(fRec.fFontID, pts)) {
+        // these are not as accurate as init_vertical_metrics :(
+        ATSFontMetrics metrics;
+        ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
+                                  &metrics);
+        pts[0].set(0, -SkFloatToScalar(metrics.ascent));
+        pts[1].set(0, -SkFloatToScalar(metrics.ascent));
+        pts[2].set(0, -SkFloatToScalar(metrics.descent));
+        pts[3].set(0, -SkFloatToScalar(metrics.descent));
+        pts[4].set(0, SkFloatToScalar(metrics.leading));    //+ or -?
+    }
+
+    SkMatrix m;
+    fRec.getSingleMatrix(&m);
+    m.mapPoints(pts, 5);
+
+    if (mx) {
+        mx->fTop = pts[0].fX;
+        mx->fAscent = pts[1].fX;
+        mx->fDescent = pts[2].fX;
+        mx->fBottom = pts[3].fX;
+        mx->fLeading = pts[4].fX;
+        // FIXME:
+        mx->fAvgCharWidth = 0;
+        mx->fXMin = 0;
+        mx->fXMax = 0;
+        mx->fXHeight = 0;
+    }
+    if (my) {
+        my->fTop = pts[0].fY;
+        my->fAscent = pts[1].fY;
+        my->fDescent = pts[2].fY;
+        my->fBottom = pts[3].fY;
+        my->fLeading = pts[4].fY;
+        // FIXME:
+        my->fAvgCharWidth = 0;
+        my->fXMin = 0;
+        my->fXMax = 0;
+        my->fXHeight = 0;
+    }
+}
+
+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    OSStatus err,result;
+
+    err = ::ATSUGlyphGetCubicPaths(
+            fStyle,glyph.fID,
+            &SkScalerContext_Mac::MoveTo,
+            &SkScalerContext_Mac::Line,
+            &SkScalerContext_Mac::Curve,
+            &SkScalerContext_Mac::Close,
+            path,&result);
+    SkASSERT(err == noErr);
+}
+
+OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
+    return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
+    return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1,
+                                    const Float32Point *pt2,
+                                    const Float32Point *pt3, void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),
+                                           F32PtToSkPoint(*pt2),
+                                           F32PtToSkPoint(*pt3));
+    return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Close(void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->close();
+    return noErr;
+}
+
+#pragma mark -
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    return NULL;
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+    SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
+    return NULL;
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    return new SkScalerContext_Mac(desc);
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    uint32_t newFontID = find_default_fontID();
+    if (newFontID == currFontID) {
+        newFontID = 0;
+    }
+    return newFontID;
+}
+
+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) {
+        familyFace->ref();
+        return const_cast<SkTypeface*>(familyFace);
+    } else {
+        return CreateTypeface_(familyName, style);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkSFNTHeader {
+    uint32_t    fVersion;
+    uint16_t    fNumTables;
+    uint16_t    fSearchRange;
+    uint16_t    fEntrySelector;
+    uint16_t    fRangeShift;
+};
+
+struct SkSFNTDirEntry {
+    uint32_t    fTag;
+    uint32_t    fChecksum;
+    uint32_t    fOffset;
+    uint32_t    fLength;
+};
+
+struct SfntHeader {
+    SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
+        ByteCount size;
+        if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
+            return;
+        }
+
+        SkAutoMalloc storage(size);
+        SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
+        if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
+            return;
+        }
+
+        fCount = SkEndian_SwapBE16(header->fNumTables);
+        fData = header;
+        storage.detach();
+    }
+
+    ~SfntHeader() {
+        sk_free(fData);
+    }
+
+    int count() const { return fCount; }
+    const SkSFNTDirEntry* entries() const {
+        return reinterpret_cast<const SkSFNTDirEntry*>
+            (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
+    }
+
+private:
+    int     fCount;
+    void*   fData;
+};
+
+int SkFontHost::CountTables(SkFontID fontID) {
+    SfntHeader header(fontID, false);
+    return header.count();
+}
+
+int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
+    SfntHeader header(fontID, true);
+    int count = header.count();
+    const SkSFNTDirEntry* entry = header.entries();
+    for (int i = 0; i < count; i++) {
+        tags[i] = SkEndian_SwapBE32(entry[i].fTag);
+    }
+    return count;
+}
+
+size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
+    ByteCount size;
+    if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
+        return 0;
+    }
+    return size;
+}
+
+size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
+                                size_t offset, size_t length, void* data) {
+    ByteCount size;
+    if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
+        return 0;
+    }
+    if (offset >= size) {
+        return 0;
+    }
+    if (offset + length > size) {
+        length = size - offset;
+    }
+    return length;
+}
diff --git a/legacy/src/ports/SkFontHost_mac_coretext.cpp b/legacy/src/ports/SkFontHost_mac_coretext.cpp
new file mode 100644
index 0000000..a612555
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_mac_coretext.cpp
@@ -0,0 +1,2005 @@
+
+/*
+ * 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 <vector>
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "SkFontHost.h"
+#include "SkCGUtils.h"
+#include "SkDescriptor.h"
+#include "SkEndian.h"
+#include "SkFloatingPoint.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTypeface_mac.h"
+#include "SkUtils.h"
+#include "SkTypefaceCache.h"
+
+class SkScalerContext_Mac;
+
+static void CFSafeRelease(CFTypeRef obj) {
+    if (obj) {
+        CFRelease(obj);
+    }
+}
+
+class AutoCFRelease : SkNoncopyable {
+public:
+    AutoCFRelease(CFTypeRef obj) : fObj(obj) {}
+    ~AutoCFRelease() { CFSafeRelease(fObj); }
+    
+private:
+    CFTypeRef fObj;
+};
+
+// inline versions of these rect helpers
+
+static bool CGRectIsEmpty_inline(const CGRect& rect) {
+    return rect.size.width <= 0 || rect.size.height <= 0;
+}
+
+static void CGRectInset_inline(CGRect* rect, CGFloat dx, CGFloat dy) {
+    rect->origin.x += dx;
+    rect->origin.y += dy;
+    rect->size.width -= dx * 2;
+    rect->size.height -= dy * 2;
+}
+
+static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
+    return rect.origin.x;
+}
+
+static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
+    return rect.origin.x + rect.size.width;
+}
+
+static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
+    return rect.origin.y;
+}
+
+static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
+    return rect.origin.y + rect.size.height;
+}
+
+static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
+    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) {
+    SkASSERT(width);
+    SkASSERT(width * sizeof(uint32_t) <= rowBytes);
+
+    if (width >= 32) {
+        while (height) {
+            sk_memset32(ptr, value, width);
+            ptr = (uint32_t*)((char*)ptr + rowBytes);
+            height -= 1;
+        }
+        return;
+    }
+
+    rowBytes -= width * sizeof(uint32_t);
+
+    if (width >= 8) {
+        while (height) {
+            int w = width;
+            do {
+                *ptr++ = value; *ptr++ = value;
+                *ptr++ = value; *ptr++ = value;
+                *ptr++ = value; *ptr++ = value;
+                *ptr++ = value; *ptr++ = value;
+                w -= 8;
+            } while (w >= 8);
+            while (--w >= 0) {
+                *ptr++ = value;
+            }
+            ptr = (uint32_t*)((char*)ptr + rowBytes);
+            height -= 1;
+        }
+    } else {
+        while (height) {
+            int w = width;
+            do {
+                *ptr++ = value;
+            } while (--w > 0);
+            ptr = (uint32_t*)((char*)ptr + rowBytes);
+            height -= 1;
+        }
+    }
+}
+
+// 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;
+
+static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
+    return pixel & 0xFF;
+}
+
+// The calls to support subpixel are present in 10.5, but are not included in
+// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
+// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
+// instance, is present in the 10.5 CoreGraphics libary, use:
+//   cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
+//   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);
+#endif
+
+static const char FONT_DEFAULT_NAME[]           = "Lucida Sans";
+
+// see Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal 
+// for original source
+static int readVersion() {
+    struct utsname info;
+    if (uname(&info) != 0) {
+        SkDebugf("uname failed\n");
+        return 0;
+    }
+    if (strcmp(info.sysname, "Darwin") != 0) {
+        SkDebugf("unexpected uname sysname %s\n", info.sysname);
+        return 0;
+    }
+    char* dot = strchr(info.release, '.');
+    if (!dot) {
+        SkDebugf("expected dot in uname release %s\n", info.release);
+        return 0;
+    }
+    int version = atoi(info.release);
+    if (version == 0) {
+        SkDebugf("could not parse uname release %s\n", info.release);
+    }
+    return version;
+}
+
+static int darwinVersion() {
+    static int darwin_version = readVersion();
+    return darwin_version;
+}
+
+static bool isLeopard() {
+    return darwinVersion() == 9;
+}
+
+static bool isSnowLeopard() {
+    return darwinVersion() == 10;
+}
+
+static bool isLion() {
+    return darwinVersion() == 11;
+}
+
+static bool isLCDFormat(unsigned format) {
+    return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
+}
+
+static CGFloat ScalarToCG(SkScalar scalar) {
+    if (sizeof(CGFloat) == sizeof(float)) {
+        return SkScalarToFloat(scalar);
+    } else {
+        SkASSERT(sizeof(CGFloat) == sizeof(double));
+        return SkScalarToDouble(scalar);
+    }
+}
+
+static SkScalar CGToScalar(CGFloat cgFloat) {
+    if (sizeof(CGFloat) == sizeof(float)) {
+        return SkFloatToScalar(cgFloat);
+    } else {
+        SkASSERT(sizeof(CGFloat) == sizeof(double));
+        return SkDoubleToScalar(cgFloat);
+    }
+}
+
+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);
+}
+
+static SkScalar getFontScale(CGFontRef cgFont) {
+    int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
+    return SkScalarInvert(SkIntToScalar(unitsPerEm));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define BITMAP_INFO_RGB     (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
+#define BITMAP_INFO_GRAY    (kCGImageAlphaNone)
+
+class Offscreen {
+public:
+    Offscreen();
+    ~Offscreen();
+
+    CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
+                      bool fgColorIsWhite, CGGlyph glyphID, size_t* rowBytesPtr);
+    
+private:
+    enum {
+        kSize = 32 * 32 * sizeof(CGRGBPixel)
+    };
+    SkAutoSMalloc<kSize> fImageStorage;
+    CGColorSpaceRef fRGBSpace;
+
+    // cached state
+    CGContextRef    fCG;
+    SkISize         fSize;
+    bool            fFgColorIsWhite;
+    bool            fDoAA;
+    bool            fDoLCD;
+
+    static int RoundSize(int dimension) {
+        return SkNextPow2(dimension);
+    }
+};
+
+Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL) {
+    fSize.set(0,0);
+}
+
+Offscreen::~Offscreen() {
+    CFSafeRelease(fCG);
+    CFSafeRelease(fRGBSpace);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isMonospace) {
+    unsigned style = SkTypeface::kNormal;
+    CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
+
+    if (traits & kCTFontBoldTrait) {
+        style |= SkTypeface::kBold;
+    }
+    if (traits & kCTFontItalicTrait) {
+        style |= SkTypeface::kItalic;
+    }
+    if (isMonospace) {
+        *isMonospace = (traits & kCTFontMonoSpaceTrait) != 0;
+    }
+    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) {
+    ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
+    SkFontID id = (SkFontID)ats;
+    if (id != 0) {
+        id &= 0x3FFFFFFF; // make top two bits 00
+        return id;
+    }
+    // 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
+        id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
+    }
+    // well-formed fonts have checksums, but as a last resort, use the pointer.
+    if (id == 0) {
+        id = (SkFontID) (uintptr_t) fontRef;
+        id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
+    }
+    CGFontRelease(cgFont);
+    return id;
+}
+
+class SkTypeface_Mac : public SkTypeface {
+public:
+    SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isMonospace,
+                   CTFontRef fontRef, const char name[])
+    : SkTypeface(style, fontID, isMonospace) {
+        SkASSERT(fontRef);
+        fFontRef = fontRef; // caller has already called CFRetain for us
+        fName.set(name);
+    }
+
+    virtual ~SkTypeface_Mac() { CFRelease(fFontRef); }
+
+    SkString    fName;
+    CTFontRef   fFontRef;
+};
+
+static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
+    SkASSERT(fontRef);
+    bool isMonospace;
+    SkTypeface::Style style = computeStyleBits(fontRef, &isMonospace);
+    SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
+
+    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;
+
+
+    // Get the state we need
+    ctFontDesc   = NULL;
+    ctFont       = NULL;
+    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);
+
+
+    // Create the font
+    if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
+        CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
+
+        CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
+        CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute,     cfTraits);
+
+        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);
+            } 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;
+}
+
+static SkTypeface* GetDefaultFace() {
+    SK_DECLARE_STATIC_MUTEX(gMutex);
+    SkAutoMutexAcquire ma(gMutex);
+
+    static SkTypeface* gDefaultFace;
+
+    if (NULL == gDefaultFace) {
+        gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
+        SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
+    }
+    return gDefaultFace;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
+/*  This function is visible on the outside. It first searches the cache, and if
+ *  not found, returns a new entry (after adding it to the cache).
+ */
+SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
+    SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
+    SkTypeface* face = SkTypefaceCache::FindByID(fontID);
+    if (face) {
+        face->ref();
+    } else {
+        face = NewFromFontRef(fontRef, NULL);
+        SkTypefaceCache::Add(face, face->style());
+        // NewFromFontRef doesn't retain the parameter, but the typeface it
+        // creates does release it in its destructor, so we balance that with
+        // a retain call here.
+        CFRetain(fontRef);
+    }
+    SkASSERT(face->getRefCnt() > 1);
+    return face;
+}
+
+struct NameStyleRec {
+    const char*         fName;
+    SkTypeface::Style   fStyle;
+};
+
+static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
+                            void* ctx) {
+    const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
+    const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
+
+    return rec->fStyle == style && mface->fName.equals(rec->fName);
+}
+
+static const char* map_css_names(const char* name) {
+    static const struct {
+        const char* fFrom;  // name the caller specified
+        const char* fTo;    // "canonical" name we map to
+    } gPairs[] = {
+        { "sans-serif", "Helvetica" },
+        { "serif",      "Times"     },
+        { "monospace",  "Courier"   }
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        if (strcmp(name, gPairs[i].fFrom) == 0) {
+            return gPairs[i].fTo;
+        }
+    }
+    return name;    // no change
+}
+
+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);
+    }
+
+    // Clone an existing typeface
+    // TODO: only clone if style matches the familyFace's style...
+    if (familyName == NULL && familyFace != NULL) {
+        familyFace->ref();
+        return const_cast<SkTypeface*>(familyFace);
+    }
+
+    if (!familyName || !*familyName) {
+        familyName = FONT_DEFAULT_NAME;
+    }
+
+    NameStyleRec rec = { familyName, style };
+    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
+
+    if (NULL == face) {
+        face = NewFromName(familyName, style);
+        if (face) {
+            SkTypefaceCache::Add(face, style);
+        } else {
+            face = GetDefaultFace();
+            face->ref();
+        }
+    }
+    return face;
+}
+
+static void flip(SkMatrix* matrix) {
+    matrix->setSkewX(-matrix->getSkewX());
+    matrix->setSkewY(-matrix->getSkewY());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GlyphRect {
+    int16_t fMinX;
+    int16_t fMinY;
+    int16_t fMaxX;
+    int16_t fMaxY;
+};
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+                                        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);
+
+
+private:
+    static void                         CTPathElement(void *info, const CGPathElement *element);
+    uint16_t                            getAdjustStart();
+    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;
+
+    friend class                        Offscreen;
+};
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+        : SkScalerContext(desc)
+        , fCTVerticalFont(NULL)
+        , fAdjustBad(NULL)
+        , fAdjustStart(0)
+        , fGeneratedBBoxes(false)
+{
+    CTFontRef ctFont = GetFontRefFromFontID(fRec.fFontID);
+    CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
+
+    // Get the state we need
+    fRec.getSingleMatrix(&fMatrix);
+    fUnitMatrix = fMatrix;
+
+    // extract the font size out of the matrix, but leave the skewing for italic
+    SkScalar reciprocal = SkScalarInvert(fRec.fTextSize);
+    fUnitMatrix.preScale(reciprocal, reciprocal);
+
+    SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
+
+    fTransform = MatrixToCGAffineTransform(fMatrix);
+
+    CGAffineTransform transform;
+    CGFloat unitFontSize;
+    if (isLeopard()) {
+        // passing 1 for pointSize to Leopard sets the font size to 1 pt.
+        // pass the CoreText size explicitly
+        transform = MatrixToCGAffineTransform(fUnitMatrix);
+        unitFontSize = SkScalarToFloat(fRec.fTextSize);
+    } else {
+        // since our matrix includes everything, we pass 1 for pointSize
+        transform = fTransform;
+        unitFontSize = 1;
+    }
+    flip(&fUnitMatrix); // flip to fix up bounds later
+    fVertical = SkToBool(fRec.fFlags & kVertical_Flag);
+    CTFontDescriptorRef ctFontDesc = NULL;
+    if (fVertical) {
+        CFMutableDictionaryRef cfAttributes = CFDictionaryCreateMutable(
+                kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
+                &kCFTypeDictionaryValueCallBacks);
+        if (cfAttributes) {
+            CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
+            CFNumberRef cfVertical = CFNumberCreate(kCFAllocatorDefault,
+                    kCFNumberSInt32Type, &ctOrientation);
+            CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute,
+                    cfVertical);
+            CFSafeRelease(cfVertical);
+            ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
+            CFRelease(cfAttributes);
+        }
+    }
+    fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform,
+            ctFontDesc);
+    CFSafeRelease(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);
+        fVerticalMatrix = fUnitMatrix;
+        if (isSnowLeopard()) {
+            SkScalar scale = SkScalarMul(fRec.fTextSize, getFontScale(fCGFont));
+            fVerticalMatrix.preScale(scale, scale);
+        } else {
+            fVerticalMatrix.preRotate(SkIntToScalar(90));
+        }
+        fVerticalMatrix.postScale(SK_Scalar1, -SK_Scalar1);
+    }
+    fGlyphCount = SkToU16(numGlyphs);
+    fDoSubPosition = SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac() {
+    delete[] fAdjustBad;
+    CFSafeRelease(fCTFont);
+    CFSafeRelease(fCTVerticalFont);
+    CFSafeRelease(fCGFont);
+}
+
+CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
+                             bool fgColorIsWhite, CGGlyph glyphID, size_t* rowBytesPtr) {
+    if (!fRGBSpace) {
+        fRGBSpace = CGColorSpaceCreateDeviceRGB();
+    }
+
+    // default to kBW_Format
+    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;
+    }
+
+    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);
+        }
+        if (fSize.fHeight < glyph.fHeight) {
+            fSize.fHeight = RoundSize(glyph.fHeight);
+        }
+
+        rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
+        void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
+        fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
+                                    rowBytes, fRGBSpace, BITMAP_INFO_RGB);
+
+        // skia handles quantization itself, so we disable this for cg to get
+        // full fractional data from them.
+        CGContextSetAllowsFontSubpixelQuantization(fCG, false);
+        CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
+
+        CGContextSetTextDrawingMode(fCG, kCGTextFill);
+        CGContextSetFont(fCG, context.fCGFont);
+        CGContextSetFontSize(fCG, 1);
+        CGContextSetTextMatrix(fCG, context.fTransform);
+
+        CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
+        CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
+        
+        // force our checks below to happen
+        fDoAA = !doAA;
+        fDoLCD = !doLCD;
+        fFgColorIsWhite = !fgColorIsWhite;
+    }
+
+    if (fDoAA != doAA) {
+        CGContextSetShouldAntialias(fCG, doAA);
+        fDoAA = doAA;
+    }
+    if (fDoLCD != doLCD) {
+        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
+
+    float subX = 0;
+    float subY = 0;
+    if (context.fDoSubPosition) {
+        subX = SkFixedToFloat(glyph.getSubXFixed());
+        subY = SkFixedToFloat(glyph.getSubYFixed());
+    }
+    if (context.fVertical) {
+        SkIPoint offset;
+        context.getVerticalOffset(glyphID, &offset);
+        subX += offset.fX;
+        subY += offset.fY;
+    }
+    CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
+                               glyph.fTop + glyph.fHeight - subY,
+                               &glyphID, 1);
+
+    SkASSERT(rowBytesPtr);
+    *rowBytesPtr = rowBytes;
+    return image;
+}
+
+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)};
+    SkPoint floatOffset;
+    fVerticalMatrix.mapPoints(&floatOffset, &trans, 1);
+    if (!isSnowLeopard()) {
+    // SnowLeopard fails to apply the font's matrix to the vertical metrics,
+    // but Lion and Leopard do. The unit matrix describes the font's matrix at
+    // point size 1. There may be some way to avoid mapping here by setting up
+    // fVerticalMatrix differently, but this works for now.
+        fUnitMatrix.mapPoints(&floatOffset, 1);
+    }
+    offset->fX = SkScalarRound(floatOffset.fX);
+    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
+    }
+    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;
+    }
+    fAdjustStart = fGlyphCount; // fallback for all fonts
+    AutoCFDataRelease hheaRef(CGFontCopyTableForTag(fCGFont, 'hhea'));
+    const uint16_t* hheaData = hheaRef.getShortPtr();
+    if (hheaData) {
+        fAdjustStart = getNumLongMetrics(hheaData);
+    }
+    return fAdjustStart;
+}
+
+/*
+ * Lion has a bug in CTFontGetBoundingRectsForGlyphs which returns a bad value
+ * in theBounds.origin.x for fonts whose numOfLogHorMetrics is less than its
+ * 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).
+ *
+ * TODO: A future optimization will compute fAdjustBad once per CGFont, and
+ * compute fAdjustBadMatrix once per font context.
+ */
+bool SkScalerContext_Mac::generateBBoxes() {
+    if (fGeneratedBBoxes) {
+        return NULL != fAdjustBad;
+    }
+    fGeneratedBBoxes = true;
+    AutoCFDataRelease headRef(CGFontCopyTableForTag(fCGFont, 'head'));
+    const uint16_t* headData = headRef.getShortPtr();
+    if (!headData) {
+        return false;
+    }
+    AutoCFDataRelease locaRef(CGFontCopyTableForTag(fCGFont, 'loca'));
+    const uint16_t* locaData = locaRef.getShortPtr();
+    if (!locaData) {
+        return false;
+    }
+    AutoCFDataRelease glyfRef(CGFontCopyTableForTag(fCGFont, 'glyf'));
+    const uint16_t* glyfData = glyfRef.getShortPtr();
+    if (!glyfData) {
+        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;
+    }
+    fAdjustBadMatrix = fMatrix;
+    flip(&fAdjustBadMatrix);
+    SkScalar fontScale = getFontScale(fCGFont);
+    fAdjustBadMatrix.preScale(fontScale, fontScale);
+    return true;
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount(void)
+{
+    return(fGlyphCount);
+}
+
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
+{   CGGlyph     cgGlyph;
+    UniChar     theChar;
+
+
+    // Validate our parameters and state
+    SkASSERT(uni             <= 0x0000FFFF);
+    SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
+
+
+    // Get the glyph
+    theChar = (UniChar) uni;
+
+    if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1))
+        cgGlyph = 0;
+
+    return(cgGlyph);
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+    this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
+    CGSize      theAdvance;
+    CGRect      theBounds;
+    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);
+        } else {
+        // 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);
+        }
+        CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
+                                   &cgGlyph, &theAdvance, 1);
+    } else {
+        CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
+                                        &cgGlyph, &theBounds, 1);
+        CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
+                                   &cgGlyph, &theAdvance, 1);
+    }
+
+    // BUG?
+    // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
+    // 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 (NULL == path || CGPathIsEmpty(path)) {
+            theBounds = CGRectMake(0, 0, 0, 0);
+        }
+        if (path) {
+            CGPathRelease(path);
+        }
+    }
+    
+    glyph->zeroMetrics();
+    glyph->fAdvanceX =  SkFloatToFixed(theAdvance.width);
+    glyph->fAdvanceY = -SkFloatToFixed(theAdvance.height);
+
+    if (CGRectIsEmpty_inline(theBounds)) {
+        return;
+    }
+    
+    if (isLeopard() && !fVertical) {
+        // Leopard does not consider the matrix skew in its bounds.
+        // Run the bounding rectangle through the skew matrix to determine
+        // the true bounds. However, this doesn't work if the font is vertical.
+        // 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);
+        fUnitMatrix.mapRect(&glyphBounds);
+        theBounds.origin.x = glyphBounds.fLeft;
+        theBounds.origin.y = glyphBounds.fTop;
+        theBounds.size.width = glyphBounds.width();
+        theBounds.size.height = glyphBounds.height();
+    }
+    // Adjust the bounds
+    //
+    // CTFontGetBoundingRectsForGlyphs ignores the font transform, so we need
+    // 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);
+
+    // Get the metrics
+    bool lionAdjustedMetrics = false;
+    if (isLion()) {
+        if (cgGlyph < fGlyphCount && cgGlyph >= getAdjustStart() 
+                    && generateBBoxes()) {
+            lionAdjustedMetrics = true;
+            SkRect adjust;
+            const GlyphRect& gRect = fAdjustBad[cgGlyph - fAdjustStart];
+            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;
+        }
+        // Lion returns fractions in the bounds
+        glyph->fWidth = sk_float_ceil2int(theBounds.size.width);
+        glyph->fHeight = sk_float_ceil2int(theBounds.size.height);
+    } else {
+        glyph->fWidth = sk_float_round2int(theBounds.size.width);
+        glyph->fHeight = sk_float_round2int(theBounds.size.height);
+    }
+    glyph->fTop      = -sk_float_round2int(CGRectGetMaxY_inline(theBounds));
+    glyph->fLeft     =  sk_float_round2int(CGRectGetMinX_inline(theBounds));
+    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.
+        getVerticalOffset(cgGlyph, &offset);
+        glyph->fLeft += offset.fX;
+        glyph->fTop += offset.fY;
+    }
+}
+
+#include "SkColorPriv.h"
+
+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 const uint8_t* getInverseTable(bool isWhite) {
+    static uint8_t gWhiteTable[256];
+    static uint8_t gTable[256];
+    static bool gInited;
+    if (!gInited) {
+        build_power_table(gWhiteTable, 1.5f);
+        build_power_table(gTable, 2.2f);
+        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);
+    }
+}
+
+static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
+    while (count > 0) {
+        uint8_t mask = 0;
+        for (int i = 7; i >= 0; --i) {
+            mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
+            if (0 == --count) {
+                break;
+            }
+        }
+        *dst++ = mask;
+    }
+}
+
+static int lerpScale(int dst, int src, int scale) {
+    return dst + (scale * (src - dst) >> 23);
+}
+
+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);
+        }
+        src = (CGRGBPixel*)((char*)src + rowBytes);
+        dst = (CGRGBPixel*)((char*)dst + rowBytes);
+    }
+}
+
+#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;
+}
+static inline int round8to6(int x) {
+    int xx = (x + 1 - (x >> 6) + (x >> 7)) >> 2;
+    SkASSERT((unsigned)xx <= 63);
+
+    int ix = x >> 2;
+    SkASSERT(SkAbs32(xx - ix) <= 1);
+    return xx;
+}
+
+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;
+
+    return SkPackARGB32(0xFF, r, g, b);
+}
+
+#define BLACK_LUMINANCE_LIMIT   0x40
+#define WHITE_LUMINANCE_LIMIT   0xA0
+
+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
+
+    // Draw the glyph
+    if (cgPixels != NULL) {
+
+#ifdef SK_USE_COLOR_LUMINANCE
+#else
+        if (invertGamma) {
+            invertGammaMask(isWhite, (uint32_t*)cgPixels,
+                            glyph.fWidth, glyph.fHeight, cgRowBytes);
+        }
+#endif
+
+        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;
+        }
+    }
+}
+
+/*
+ *  Our subpixel resolution is only 2 bits in each direction, so a scale of 4
+ *  seems sufficient, and possibly even correct, to allow the hinted outline
+ *  to be subpixel positioned.
+ */
+#define kScaleForSubPixelPositionHinting  4
+
+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
+    CTFontRef font = fCTFont;
+    float scaleX = 1;
+    float scaleY = 1;
+
+    /*
+     *  For subpixel positioning, we want to return an unhinted outline, so it
+     *  can be positioned nicely at fractional offsets. However, we special-case
+     *  if the baseline of the (horizontal) text is axis-aligned. In those cases
+     *  we want to retain hinting in the direction orthogonal to the baseline.
+     *  e.g. for horizontal baseline, we want to retain hinting in Y.
+     *  The way we remove hinting is to scale the font by some value (4) in that
+     *  direction, ask for the path, and then scale the path back down.
+     */
+    if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
+        SkMatrix m;
+        fRec.getSingleMatrix(&m);
+
+        // start out by assuming that we want no hining in X and Y
+        scaleX = scaleY = 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
+                break;
+            case kY_SkAxisAlignment:
+                scaleX = 1; // want hinting in the X direction
+                break;
+            default:
+                break;
+        }
+
+        CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
+        // 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);
+
+    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));
+        path->transform(m);
+        // balance the call to CTFontCreateCopyWithAttributes
+        CFRelease(font);
+    }
+    if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
+        SkIPoint offset;
+        getVerticalOffset(cgGlyph, &offset);
+        path->offset(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY));
+    }
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
+                                              SkPaint::FontMetrics* my) {
+    CGRect theBounds = CTFontGetBoundingBox(fCTFont);
+
+    SkPaint::FontMetrics theMetrics;
+    theMetrics.fTop          = CGToScalar(-CGRectGetMaxY_inline(theBounds));
+    theMetrics.fAscent       = CGToScalar(-CTFontGetAscent(fCTFont));
+    theMetrics.fDescent      = CGToScalar( CTFontGetDescent(fCTFont));
+    theMetrics.fBottom       = CGToScalar(-CGRectGetMinY_inline(theBounds));
+    theMetrics.fLeading      = CGToScalar( CTFontGetLeading(fCTFont));
+    theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
+    theMetrics.fXMin         = CGToScalar( CGRectGetMinX_inline(theBounds));
+    theMetrics.fXMax         = CGToScalar( CGRectGetMaxX_inline(theBounds));
+    theMetrics.fXHeight      = CGToScalar( CTFontGetXHeight(fCTFont));
+
+    if (mx != NULL) {
+        *mx = theMetrics;
+    }
+    if (my != NULL) {
+        *my = theMetrics;
+    }
+}
+
+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);
+            break;
+
+        case kCGPathElementAddLineToPoint:
+            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);
+            break;
+
+        case kCGPathElementAddCurveToPoint:
+            skPath->cubicTo(element->points[0].x, -element->points[0].y,
+                            element->points[1].x, -element->points[1].y,
+                            element->points[2].x, -element->points[2].y);
+            break;
+
+        case kCGPathElementCloseSubpath:
+            skPath->close();
+            break;
+
+        default:
+            SkDEBUGFAIL("Unknown path element!");
+            break;
+        }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Returns NULL on failure
+// Call must still manage its ownership of provider
+static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
+    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);
+    if (NULL == provider) {
+        return NULL;
+    }
+    AutoCGDataProviderRelease ar(provider);
+    return create_from_dataProvider(provider);
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    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) {
+    glyphToUnicode->setCount(glyphCount);
+    SkUnichar* out = glyphToUnicode->begin();
+    sk_bzero(out, glyphCount * sizeof(SkUnichar));
+    UniChar unichar = 0;
+    while (glyphCount > 0) {
+        CGGlyph glyph;
+        if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
+            out[glyph] = unichar;
+            --glyphCount;
+        }
+        if (++unichar == 0) {
+            break;
+        }
+    }
+}
+
+// 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);
+    if (!charSet) {
+        populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
+        return;
+    }
+    CFDataRef bitmap = CFCharacterSetCreateBitmapRepresentation(
+        kCFAllocatorDefault, charSet);
+    if (!bitmap) {
+        return;
+    }
+    CFIndex length = CFDataGetLength(bitmap);
+    if (!length) {
+        CFSafeRelease(bitmap);
+        return;
+    }
+    if (length > 8192) {
+        // TODO: Add support for Unicode above 0xFFFF
+        // Consider only the BMP portion of the Unicode character points.
+        // The bitmap may contain other planes, up to plane 16.
+        // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
+        length = 8192;
+    }
+    const UInt8* bits = CFDataGetBytePtr(bitmap);
+    glyphToUnicode->setCount(glyphCount);
+    SkUnichar* out = glyphToUnicode->begin();
+    sk_bzero(out, glyphCount * sizeof(SkUnichar));
+    for (int i = 0; i < length; i++) {
+        int mask = bits[i];
+        if (!mask) {
+            continue;
+        }
+        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)) {
+                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);
+    *data = sk_float_round2int(advance.width);
+    return true;
+}
+
+// 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);
+    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()));
+    info->fMultiMaster = false;
+    CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
+    info->fLastGlyphID = SkToU16(glyphCount - 1);
+    info->fEmSize = CTFontGetUnitsPerEm(ctFont);
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+        populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
+    }
+
+    info->fStyle = 0;
+
+    // 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. 
+    if (!GetTableSize(fontID, 'glyf') || !GetTableSize(fontID, 'loca')) {
+        info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
+        info->fItalicAngle = 0;
+        info->fAscent = 0;
+        info->fDescent = 0;
+        info->fStemV = 0;
+        info->fCapHeight = 0;
+        info->fBBox = SkIRect::MakeEmpty();
+        CFSafeRelease(ctFont);
+        return info;
+    }
+
+    info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+    CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
+    if (symbolicTraits & kCTFontMonoSpaceTrait) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+    }
+    if (symbolicTraits & kCTFontItalicTrait) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+    }
+    CTFontStylisticClass stylisticClass = symbolicTraits &
+            kCTFontClassMaskTrait;
+    if (stylisticClass & kCTFontSymbolicClass) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+    }
+    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);
+    CGRect bbox = CTFontGetBoundingBox(ctFont);
+    info->fBBox = SkIRect::MakeXYWH(bbox.origin.x, bbox.origin.y,
+        bbox.size.width, bbox.size.height);
+
+    // 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;
+    static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
+    const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
+    CGGlyph glyphs[count];
+    CGRect boundingRects[count];
+    if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
+        CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
+            glyphs, boundingRects, count);
+        for (size_t i = 0; i < count; i++) {
+            int16_t width = boundingRects[i].size.width;
+            if (width > 0 && width < min_width) {
+                min_width = width;
+                info->fStemV = min_width;
+            }
+        }
+    }
+
+    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) {
+        if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
+            skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
+            info->fGlyphWidths->fAdvance.append(1, &min_width);
+            skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
+                        SkAdvancedTypefaceMetrics::WidthRange::kDefault);
+        } else {
+            info->fGlyphWidths.reset(
+                skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont,
+                               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++);
+    }
+    return sum;
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+    // get table tags
+    int tableCount = CountTables(uniqueID);
+    SkTDArray<SkFontTableTag> tableTags;
+    tableTags.setCount(tableCount);
+    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]);
+        totalSize += (tableSize + 3) & ~3;
+        *tableSizes.append() = tableSize;
+    }
+
+    // reserve memory for stream, and zero it (tables must be zero padded)
+    SkMemoryStream* stream = new SkMemoryStream(totalSize);
+    char* dataStart = (char*)stream->getMemoryBase();
+    sk_bzero(dataStart, totalSize);
+    char* dataPtr = dataStart;
+
+    // compute font header entries
+    uint16_t entrySelector = 0;
+    uint16_t searchRange = 1;
+    while (searchRange < tableCount >> 1) {
+        entrySelector++;
+        searchRange <<= 1;
+    }
+    searchRange <<= 4;
+    uint16_t rangeShift = (tableCount << 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 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);
+        dataPtr += (tableSize + 3) & ~3;
+        ++entry;
+    }
+
+    return stream;
+}
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
+                               int32_t* index) {
+    SkDEBUGFAIL("SkFontHost::GetFileName unimplemented");
+    return(0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+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);
+}
+
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    return new SkScalerContext_Mac(desc);
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    SkFontID nextFontID = 0;
+    SkTypeface* face = GetDefaultFace();
+    if (face->uniqueID() != currFontID) {
+        nextFontID = face->uniqueID();
+    }
+    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) {
+    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);
+
+#ifdef SK_USE_COLOR_LUMINANCE
+    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;
+        } else {
+            rec->fMaskFormat = SkMask::kA8_Format;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+int SkFontHost::CountTables(SkFontID fontID) {
+    CTFontRef ctFont = GetFontRefFromFontID(fontID);
+    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);
+    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);
+        }
+    }
+    return count;
+}
+
+// If, as is the case with web fonts, the CTFont data isn't available,
+// 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);
+    if (NULL == data) {
+        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);
+    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);
+    if (NULL == srcData) {
+        return 0;
+    }
+
+    AutoCFRelease ar(srcData);
+
+    size_t srcSize = CFDataGetLength(srcData);
+    if (offset >= srcSize) {
+        return 0;
+    }
+
+    if ((offset + length) > srcSize) {
+        length = srcSize - offset;
+    }
+
+    if (dst) {
+        memcpy(dst, CFDataGetBytePtr(srcData) + offset, length);
+    }
+    return length;
+}
diff --git a/legacy/src/ports/SkFontHost_none.cpp b/legacy/src/ports/SkFontHost_none.cpp
new file mode 100644
index 0000000..e79926d
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_none.cpp
@@ -0,0 +1,76 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkFontHost.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;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream*) {
+    SkDEBUGFAIL("SkFontHost::CreateTypeface unimplemented");
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(char const*) {
+    SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
+    return NULL;
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
+    SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
+    return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkStream* SkFontHost::OpenStream(uint32_t uniqueID) {
+    SkDEBUGFAIL("SkFontHost::OpenStream unimplemented");
+    return NULL;
+}
+
+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) {
+    SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    SkDEBUGFAIL("SkFontHost::CreateScalarContext unimplemented");
+    return NULL;
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    return 0;
+}
+
+
diff --git a/legacy/src/ports/SkFontHost_sandbox_none.cpp b/legacy/src/ports/SkFontHost_sandbox_none.cpp
new file mode 100644
index 0000000..a52bbff
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_sandbox_none.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkFontHost.h"
+#include "SkTypeface.h"
+
+//static
+void SkFontHost::EnsureTypefaceAccessible(const SkTypeface& typeface) {
+    //No sandbox, nothing to do.
+}
diff --git a/legacy/src/ports/SkFontHost_simple.cpp b/legacy/src/ports/SkFontHost_simple.cpp
new file mode 100644
index 0000000..eef4dd7
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_simple.cpp
@@ -0,0 +1,644 @@
+
+/*
+ * 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 "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkMMapStream.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTSearch.h"
+#include <stdio.h>
+
+#ifdef SK_BUILD_FOR_MAC
+    #define SK_FONT_FILE_PREFIX     "/Library/Fonts/"
+#else
+    #define SK_FONT_FILE_PREFIX          "/skimages/"
+#endif
+
+bool find_name_and_attributes(SkStream* stream, SkString* name,
+                              SkTypeface::Style* style, bool* isFixedWidth);
+
+static void GetFullPathForSysFonts(SkString* full, const char name[]) {
+    full->set(SK_FONT_FILE_PREFIX);
+    full->append(name);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct FamilyRec;
+
+/*  This guy holds a mapping of a name -> family, used for looking up fonts.
+    Since it is stored in a stretchy array that doesn't preserve object
+    semantics, we don't use constructor/destructors, but just have explicit
+    helpers to manage our internal bookkeeping.
+*/
+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);
+        fFamily = family;   // we don't own this, so just record the referene
+    }
+
+    void destruct() {
+        free((char*)fName);
+        // we don't own family, so just ignore our reference
+    }
+};
+
+// we use atomic_inc to grow this for each typeface we create
+static int32_t gUniqueFontID;
+
+// this is the mutex that protects these globals
+SK_DECLARE_STATIC_MUTEX(gFamilyMutex);
+static FamilyRec* gFamilyHead;
+static SkTDArray<NameFamilyPair> gNameList;
+
+struct FamilyRec {
+    FamilyRec*  fNext;
+    SkTypeface* fFaces[4];
+
+    FamilyRec()
+    {
+        fNext = gFamilyHead;
+        memset(fFaces, 0, sizeof(fFaces));
+        gFamilyHead = this;
+    }
+};
+
+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];
+    }
+    // look for a matching bold
+    style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
+    if (faces[style] != NULL) {
+        return faces[style];
+    }
+    // look for the plain
+    if (faces[SkTypeface::kNormal] != NULL) {
+        return faces[SkTypeface::kNormal];
+    }
+    // look for anything
+    for (int i = 0; i < 4; i++) {
+        if (faces[i] != NULL) {
+            return faces[i];
+        }
+    }
+    // should never get here, since the faces list should not be empty
+    SkDEBUGFAIL("faces list is empty");
+    return NULL;
+}
+
+static FamilyRec* find_family(const SkTypeface* member) {
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            if (curr->fFaces[i] == member) {
+                return curr;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+/*  Returns the matching typeface, or NULL. If a typeface is found, its refcnt
+    is not modified.
+ */
+static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            SkTypeface* face = curr->fFaces[i];
+            if (face != NULL && face->uniqueID() == uniqueID) {
+                return face;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+/*  Remove reference to this face from its family. If the resulting family
+    is empty (has no faces), return that family, otherwise return NULL
+*/
+static FamilyRec* remove_from_family(const SkTypeface* face) {
+    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;
+        }
+    }
+    return family;  // return the empty family
+}
+
+// maybe we should make FamilyRec be doubly-linked
+static void detach_and_delete_family(FamilyRec* family) {
+    FamilyRec* curr = gFamilyHead;
+    FamilyRec* prev = NULL;
+
+    while (curr != NULL) {
+        FamilyRec* next = curr->fNext;
+        if (curr == family) {
+            if (prev == NULL) {
+                gFamilyHead = next;
+            } else {
+                prev->fNext = next;
+            }
+            SkDELETE(family);
+            return;
+        }
+        prev = curr;
+        curr = next;
+    }
+    SkDEBUGFAIL("Yikes, couldn't find family in our list to remove/delete");
+}
+
+static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
+    NameFamilyPair* list = gNameList.begin();
+    int             count = gNameList.count();
+
+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
+
+    if (index >= 0) {
+        return find_best_face(list[index].fFamily, style);
+    }
+    return NULL;
+}
+
+static SkTypeface* find_typeface(const SkTypeface* familyMember,
+                                 SkTypeface::Style style) {
+    const FamilyRec* family = find_family(familyMember);
+    return family ? find_best_face(family, style) : NULL;
+}
+
+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);
+    }
+}
+
+static void remove_from_names(FamilyRec* emptyFamily)
+{
+#ifdef SK_DEBUG
+    for (int i = 0; i < 4; i++) {
+        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];
+        if (pair->fFamily == emptyFamily) {
+            pair->destruct();
+            list.remove(i);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FamilyTypeface : public SkTypeface {
+public:
+    FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember)
+    : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) {
+        fIsSysFont = sysFont;
+
+        SkAutoMutexAcquire  ac(gFamilyMutex);
+
+        FamilyRec* rec = NULL;
+        if (familyMember) {
+            rec = find_family(familyMember);
+            SkASSERT(rec);
+        } else {
+            rec = SkNEW(FamilyRec);
+        }
+        rec->fFaces[style] = this;
+    }
+
+    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);
+        if (NULL != family) {
+            remove_from_names(family);
+            detach_and_delete_family(family);
+        }
+    }
+
+    bool isSysFont() const { return fIsSysFont; }
+
+    virtual SkStream* openStream() = 0;
+    virtual const char* getUniqueString() const = 0;
+    virtual const char* getFilePath() const = 0;
+
+private:
+    bool    fIsSysFont;
+
+    typedef SkTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTypeface : public FamilyTypeface {
+public:
+    StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
+                   SkStream* stream)
+    : INHERITED(style, sysFont, familyMember) {
+        SkASSERT(stream);
+        stream->ref();
+        fStream = stream;
+    }
+    virtual ~StreamTypeface() {
+        fStream->unref();
+    }
+
+    // overrides
+    virtual SkStream* openStream() {
+        // we just ref our existing stream, since the caller will call unref()
+        // when they are through
+        fStream->ref();
+        return fStream;
+    }
+    virtual const char* getUniqueString() const { return NULL; }
+    virtual const char* getFilePath() const { return NULL; }
+
+private:
+    SkStream* fStream;
+
+    typedef FamilyTypeface INHERITED;
+};
+
+class FileTypeface : public FamilyTypeface {
+public:
+    FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
+                 const char path[])
+    : INHERITED(style, sysFont, familyMember) {
+        SkString fullpath;
+
+        if (sysFont) {
+            GetFullPathForSysFonts(&fullpath, path);
+            path = fullpath.c_str();
+        }
+        fPath.set(path);
+    }
+
+    // overrides
+    virtual SkStream* openStream() {
+        SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
+
+        // check for failure
+        if (stream->getLength() <= 0) {
+            SkDELETE(stream);
+            // maybe MMAP isn't supported. try FILE
+            stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
+            if (stream->getLength() <= 0) {
+                SkDELETE(stream);
+                stream = NULL;
+            }
+        }
+        return stream;
+    }
+    virtual const char* getUniqueString() const {
+        const char* str = strrchr(fPath.c_str(), '/');
+        if (str) {
+            str += 1;   // skip the '/'
+        }
+        return str;
+    }
+    virtual const char* getFilePath() const {
+        return fPath.c_str();
+    }
+
+private:
+    SkString fPath;
+
+    typedef FamilyTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool get_name_and_style(const char path[], SkString* name,
+                               SkTypeface::Style* style, bool isExpected) {
+    SkString        fullpath;
+    GetFullPathForSysFonts(&fullpath, path);
+
+    SkMMAPStream stream(fullpath.c_str());
+    if (stream.getLength() > 0) {
+        return find_name_and_attributes(&stream, name, style, NULL);
+    }
+    else {
+        SkFILEStream stream(fullpath.c_str());
+        if (stream.getLength() > 0) {
+            return find_name_and_attributes(&stream, name, style, NULL);
+        }
+    }
+
+    if (isExpected) {
+        SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
+    }
+    return false;
+}
+
+// used to record our notion of the pre-existing fonts
+struct FontInitRec {
+    const char*         fFileName;
+    const char* const*  fNames;     // null-terminated list
+};
+
+static const char* gSansNames[] = {
+    "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
+};
+
+static const char* gSerifNames[] = {
+    "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
+    "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
+};
+
+static const char* gMonoNames[] = {
+    "monospace", "courier", "courier new", "monaco", NULL
+};
+
+// deliberately empty, but we use the address to identify fallback fonts
+static const char* gFBNames[] = { NULL };
+
+/*  Fonts must be grouped by family, with the first font in a family having the
+    list of names (even if that list is empty), and the following members having
+    null for the list. The names list must be NULL-terminated
+*/
+static const FontInitRec gSystemFonts[] = {
+    { "Arial.ttf",              gSansNames  },
+    { "Times.ttf",              gSerifNames  },
+    { "samplefont.ttf",              gSansNames  },
+};
+
+#define DEFAULT_NAMES   gSansNames
+
+// these globals are assigned (once) by load_system_fonts()
+static FamilyRec* gDefaultFamily;
+static SkTypeface* gDefaultNormal;
+
+/*  This is sized conservatively, assuming that it will never be a size issue.
+    It will be initialized in load_system_fonts(), and will be filled with the
+    fontIDs that can be used for fallback consideration, in sorted order (sorted
+    meaning element[0] should be used first, then element[1], etc. When we hit
+    a fontID==0 in the array, the list is done, hence our allocation size is
+    +1 the total number of possible system fonts. Also see NextLogicalFont().
+ */
+static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
+
+/*  Called once (ensured by the sentinel check at the beginning of our body).
+    Initializes all the globals, and register the system fonts.
+ */
+static void load_system_fonts() {
+    // check if we've already be called
+    if (NULL != gDefaultNormal) {
+        return;
+    }
+
+    const FontInitRec* rec = gSystemFonts;
+    SkTypeface* firstInFamily = NULL;
+    int fallbackCount = 0;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+        // if we're the first in a new family, clear firstInFamily
+        if (rec[i].fNames != NULL) {
+            firstInFamily = NULL;
+        }
+
+        SkString name;
+        SkTypeface::Style style;
+
+        // we expect all the fonts, except the "fallback" fonts
+        bool isExpected = (rec[i].fNames != gFBNames);
+        if (!get_name_and_style(rec[i].fFileName, &name, &style, isExpected)) {
+            continue;
+        }
+
+        SkTypeface* tf = SkNEW_ARGS(FileTypeface,
+                                    (style,
+                                     true,  // system-font (cannot delete)
+                                     firstInFamily, // what family to join
+                                     rec[i].fFileName) // filename
+                                    );
+
+        if (rec[i].fNames != NULL) {
+            // see if this is one of our fallback fonts
+            if (rec[i].fNames == gFBNames) {
+            //    SkDebugf("---- adding %s as fallback[%d] fontID %d\n",
+            //             rec[i].fFileName, fallbackCount, tf->uniqueID());
+                gFallbackFonts[fallbackCount++] = tf->uniqueID();
+            }
+
+            firstInFamily = tf;
+            FamilyRec* family = find_family(tf);
+            const char* const* names = rec[i].fNames;
+
+            // record the default family if this is it
+            if (names == DEFAULT_NAMES) {
+                gDefaultFamily = family;
+            }
+            // add the names to map to this family
+            while (*names) {
+                add_name(*names, family);
+                names += 1;
+            }
+        }
+    }
+
+    // do this after all fonts are loaded. This is our default font, and it
+    // acts as a sentinel so we only execute load_system_fonts() once
+    gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
+    // now terminate our fallback list with the sentinel value
+    gFallbackFonts[fallbackCount] = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    const char* name = ((FamilyTypeface*)face)->getUniqueString();
+
+    stream->write8((uint8_t)face->style());
+
+    if (NULL == name || 0 == *name) {
+        stream->writePackedUInt(0);
+//        SkDebugf("--- fonthost serialize null\n");
+    } else {
+        uint32_t len = strlen(name);
+        stream->writePackedUInt(len);
+        stream->write(name, len);
+//      SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
+    }
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    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);
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+    }
+
+    // we ref(), since the symantic is to return a new instance
+    tf->ref();
+    return tf;
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t fontID) {
+    SkAutoMutexAcquire  ac(gFamilyMutex);
+
+    FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
+    SkStream* stream = tf ? tf->openStream() : NULL;
+
+    if (stream && stream->getLength() == 0) {
+        stream->unref();
+        stream = NULL;
+    }
+    return stream;
+}
+
+#if 0
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
+    SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
+    return NULL;
+}
+#endif
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
+                               int32_t* index) {
+    SkAutoMutexAcquire  ac(gFamilyMutex);
+
+    FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
+    const char* src = tf ? tf->getFilePath() : NULL;
+
+    if (src) {
+        size_t size = strlen(src);
+        if (path) {
+            memcpy(path, src, SkMin32(size, length));
+        }
+        if (index) {
+            *index = 0; // we don't have collections (yet)
+        }
+        return size;
+    } else {
+        return 0;
+    }
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    load_system_fonts();
+
+    /*  First see if fontID is already one of our fallbacks. If so, return
+        its successor. If fontID is not in our list, then return the first one
+        in our list. Note: list is zero-terminated, and returning zero means
+        we have no more fonts to use for fallbacks.
+     */
+    const uint32_t* list = gFallbackFonts;
+    for (int i = 0; list[i] != 0; i++) {
+        if (list[i] == currFontID) {
+            return list[i+1];
+        }
+    }
+    return list[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    if (NULL == stream || stream->getLength() <= 0) {
+        return NULL;
+    }
+
+    SkTypeface::Style style;
+    if (find_name_and_attributes(stream, NULL, &style, NULL)) {
+        return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream));
+    } else {
+        return NULL;
+    }
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
+    SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
+    // since we created the stream, we let go of our ref() here
+    stream->unref();
+    return face;
+}
+
diff --git a/legacy/src/ports/SkFontHost_tables.cpp b/legacy/src/ports/SkFontHost_tables.cpp
new file mode 100644
index 0000000..9878119
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_tables.cpp
@@ -0,0 +1,206 @@
+
+/*
+ * 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 "SkFontHost.h"
+#include "SkStream.h"
+
+struct SkSFNTHeader {
+    uint32_t    fVersion;
+    uint16_t    fNumTables;
+    uint16_t    fSearchRange;
+    uint16_t    fEntrySelector;
+    uint16_t    fRangeShift;
+};
+
+struct SkTTCFHeader {
+    uint32_t    fTag;
+    uint32_t    fVersion;
+    uint32_t    fNumOffsets;
+    uint32_t    fOffset0;   // the first of N (fNumOffsets)
+};
+
+union SkSharedTTHeader {
+    SkSFNTHeader    fSingle;
+    SkTTCFHeader    fCollection;
+};
+
+struct SkSFNTDirEntry {
+    uint32_t    fTag;
+    uint32_t    fChecksum;
+    uint32_t    fOffset;
+    uint32_t    fLength;
+};
+
+/** Return the number of tables, or if this is a TTC (collection), return the
+    number of tables in the first element of the collection. In either case,
+    if offsetToDir is not-null, set it to the offset to the beginning of the
+    table headers (SkSFNTDirEntry), relative to the start of the stream.
+
+    On an error, return 0 for number of tables, and ignore offsetToDir
+ */
+static int count_tables(SkStream* stream, size_t* offsetToDir = NULL) {
+    SkSharedTTHeader shared;
+    if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) {
+        return 0;
+    }
+
+    // by default, SkSFNTHeader is at the start of the stream
+    size_t offset = 0;
+
+    // if we're really a collection, the first 4-bytes will be 'ttcf'
+    uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag);
+    if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
+        if (shared.fCollection.fNumOffsets == 0) {
+            return 0;
+        }
+        // this is the offset to the first local SkSFNTHeader
+        offset = SkEndian_SwapBE32(shared.fCollection.fOffset0);
+        stream->rewind();
+        if (stream->skip(offset) != offset) {
+            return 0;
+        }
+        if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) {
+            return 0;
+        }
+    }
+
+    if (offsetToDir) {
+        // add the size of the header, so we will point to the DirEntries
+        *offsetToDir = offset + sizeof(SkSFNTHeader);
+    }
+    return SkEndian_SwapBE16(shared.fSingle.fNumTables);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SfntHeader {
+    SfntHeader() : fCount(0), fDir(NULL) {}
+    ~SfntHeader() { sk_free(fDir); }
+
+    /** If it returns true, then fCount and fDir are properly initialized.
+        Note: fDir will point to the raw array of SkSFNTDirEntry values,
+        meaning they will still be in the file's native endianness (BE).
+
+        fDir will be automatically freed when this object is destroyed
+     */
+    bool init(SkStream* stream) {
+        size_t offsetToDir;
+        fCount = count_tables(stream, &offsetToDir);
+        if (0 == fCount) {
+            return false;
+        }
+
+        stream->rewind();
+        if (stream->skip(offsetToDir) != offsetToDir) {
+            return false;
+        }
+
+        size_t size = fCount * sizeof(SkSFNTDirEntry);
+        fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size));
+        return stream->read(fDir, size) == size;
+    }
+
+    int             fCount;
+    SkSFNTDirEntry* fDir;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkFontHost::CountTables(SkFontID fontID) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+
+    SkAutoUnref au(stream);
+    return count_tables(stream);
+}
+
+int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+
+    SkAutoUnref au(stream);
+    SfntHeader  header;
+    if (!header.init(stream)) {
+        return 0;
+    }
+
+    for (int i = 0; i < header.fCount; i++) {
+        tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag);
+    }
+    return header.fCount;
+}
+
+size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+
+    SkAutoUnref au(stream);
+    SfntHeader  header;
+    if (!header.init(stream)) {
+        return 0;
+    }
+
+    for (int i = 0; i < header.fCount; i++) {
+        if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
+            return SkEndian_SwapBE32(header.fDir[i].fLength);
+        }
+    }
+    return 0;
+}
+
+size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
+                                size_t offset, size_t length, void* data) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+
+    SkAutoUnref au(stream);
+    SfntHeader  header;
+    if (!header.init(stream)) {
+        return 0;
+    }
+
+    for (int i = 0; i < header.fCount; i++) {
+        if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
+            size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset);
+            size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength);
+            // now sanity check the caller's offset/length
+            if (offset >= realLength) {
+                return 0;
+            }
+            // if the caller is trusting the length from the file, then a
+            // hostile file might choose a value which would overflow offset +
+            // length.
+            if (offset + length < offset) {
+                return 0;
+            }
+            if (offset + length > realLength) {
+                length = realLength - offset;
+            }
+            // skip the stream to the part of the table we want to copy from
+            stream->rewind();
+            size_t bytesToSkip = realOffset + offset;
+            if (stream->skip(bytesToSkip) != bytesToSkip) {
+                return 0;
+            }
+            if (stream->read(data, length) != length) {
+                return 0;
+            }
+            return length;
+        }
+    }
+    return 0;
+}
+
diff --git a/legacy/src/ports/SkFontHost_win.cpp b/legacy/src/ports/SkFontHost_win.cpp
new file mode 100755
index 0000000..ec7e1b0
--- /dev/null
+++ b/legacy/src/ports/SkFontHost_win.cpp
@@ -0,0 +1,1347 @@
+
+/*
+ * 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 "SkColorFilter.h"
+#include "SkString.h"
+#include "SkEndian.h"
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkStream.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"
+
+// always packed xxRRGGBB
+typedef uint32_t SkGdiRGB;
+
+template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
+    return (T*)((char*)ptr + byteOffset);
+}
+
+// define this in your Makefile or .gyp to enforce AA requests
+// which GDI ignores at small sizes. This flag guarantees AA
+// for rotated text, regardless of GDI's notions.
+//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
+
+// client3d has to undefine this for now
+#define CAN_USE_LOGFONT_NAME
+
+static bool isLCD(const SkScalerContext::Rec& rec) {
+    return SkMask::kLCD16_Format == rec.fMaskFormat ||
+           SkMask::kLCD32_Format == rec.fMaskFormat;
+}
+
+static bool bothZero(SkScalar a, SkScalar b) {
+    return 0 == a && 0 == b;
+}
+
+// returns false if there is any non-90-rotation or skew
+static bool isAxisAligned(const SkScalerContext::Rec& rec) {
+    return 0 == rec.fPreSkewX &&
+           (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
+            bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
+}
+
+static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
+#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
+    // What we really want to catch is when GDI will ignore the AA request and give
+    // us BW instead. Smallish rotated text is one heuristic, so this code is just
+    // an approximation. We shouldn't need to do this for larger sizes, but at those
+    // sizes, the quality difference gets less and less between our general
+    // scanconverter and GDI's.
+    if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
+        return true;
+    }
+#endif
+    // false means allow GDI to generate the bits
+    return false;
+}
+
+using namespace skia_advanced_typeface_metrics_utils;
+
+static const uint16_t BUFFERSIZE = (16384 - 32);
+static uint8_t glyphbuf[BUFFERSIZE];
+
+/**
+ *  Since LOGFONT wants its textsize as an int, and we support fractional sizes,
+ *  and since we have a cache of LOGFONTs for our tyepfaces, we always set the
+ *  lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the
+ *  actual requested size.
+ */
+static const int gCanonicalTextSize = 64;
+
+static void make_canonical(LOGFONT* lf) {
+    lf->lfHeight = -gCanonicalTextSize;
+    lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
+    lf->lfCharSet = DEFAULT_CHARSET;
+//    lf->lfClipPrecision = 64;
+}
+
+static SkTypeface::Style get_style(const LOGFONT& lf) {
+    unsigned style = 0;
+    if (lf.lfWeight >= FW_BOLD) {
+        style |= SkTypeface::kBold;
+    }
+    if (lf.lfItalic) {
+        style |= SkTypeface::kItalic;
+    }
+    return static_cast<SkTypeface::Style>(style);
+}
+
+static void setStyle(LOGFONT* lf, SkTypeface::Style style) {
+    lf->lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+    lf->lfItalic = ((style & SkTypeface::kItalic) != 0);
+}
+
+static inline FIXED SkFixedToFIXED(SkFixed x) {
+    return *(FIXED*)(&x);
+}
+
+static inline FIXED SkScalarToFIXED(SkScalar x) {
+    return SkFixedToFIXED(SkScalarToFixed(x));
+}
+
+static unsigned calculateGlyphCount(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'));
+    uint16_t glyphs;
+    if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
+        return SkEndian_SwapBE16(glyphs);
+    }
+
+    // Binary search for glyph count.
+    static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
+    int32_t max = SK_MaxU16 + 1;
+    int32_t min = 0;
+    GLYPHMETRICS gm;
+    while (min < max) {
+        int32_t mid = min + ((max - min) / 2);
+        if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
+                             NULL, &mat2) == GDI_ERROR) {
+            max = mid;
+        } else {
+            min = mid + 1;
+        }
+    }
+    SkASSERT(min == max);
+    return min;
+}
+
+class LogFontTypeface : public SkTypeface {
+public:
+    LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf) :
+      SkTypeface(style, fontID, false), fLogFont(lf) {}
+
+    LOGFONT fLogFont;
+
+    static LogFontTypeface* Create(const LOGFONT& lf) {
+        SkTypeface::Style style = get_style(lf);
+        SkFontID fontID = SkTypefaceCache::NewFontID();
+        return new LogFontTypeface(style, fontID, lf);
+    }
+};
+
+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);
+    const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
+
+    return get_style(lface->fLogFont) == requestedStyle &&
+           !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
+}
+
+/**
+ *  This guy is public. It first searches the cache, and if a match is not found,
+ *  it creates a new face.
+ */
+SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
+    LOGFONT lf = origLF;
+    make_canonical(&lf);
+    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
+    if (NULL == face) {
+        face = LogFontTypeface::Create(lf);
+        SkTypefaceCache::Add(face, get_style(lf));
+    }
+    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;
+    }
+}
+
+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;
+}
+
+static void ensure_typeface_accessible(SkFontID fontID) {
+    LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
+    if (face) {
+        SkFontHost::EnsureTypefaceAccessible(*face);
+    }
+}
+
+static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
+    LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
+    if (face) {
+        *lf = face->fLogFont;
+    } else {
+        sk_bzero(lf, sizeof(LOGFONT));
+    }
+}
+
+// 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().
+static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
+                                      SkTDArray<SkUnichar>* glyphToUnicode) {
+    DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
+    if (!glyphSetBufferSize) {
+        return;
+    }
+
+    SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
+    GLYPHSET* glyphSet =
+        reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
+    if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
+        return;
+    }
+
+    glyphToUnicode->setCount(glyphCount);
+    memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
+    for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
+        // There is no guarantee that within a Unicode range, the corresponding
+        // glyph id in a font file are continuous. So, even if we have ranges,
+        // we can't just use the first and last entry of the range to compute
+        // result. We need to enumerate them one by one.
+        int count = glyphSet->ranges[i].cGlyphs;
+        SkAutoTArray<WCHAR> chars(count + 1);
+        chars[count] = 0;  // termintate string
+        SkAutoTArray<WORD> glyph(count);
+        for (USHORT j = 0; j < count; ++j) {
+            chars[j] = glyphSet->ranges[i].wcLow + j;
+        }
+        GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
+                         GGI_MARK_NONEXISTING_GLYPHS);
+        // If the glyph ID is valid, and the glyph is not mapped, then we will
+        // fill in the char id into the vector. If the glyph is mapped already,
+        // skip it.
+        // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
+        // font cache, then generate this mapping table from there. It's
+        // unlikely to have collisions since glyph reuse happens mostly for
+        // different Unicode pages.
+        for (USHORT j = 0; j < count; ++j) {
+            if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
+                (*glyphToUnicode)[glyph[j]] == 0) {
+                (*glyphToUnicode)[glyph[j]] = chars[j];
+            }
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+static int alignTo32(int n) {
+    return (n + 31) & ~31;
+}
+
+struct MyBitmapInfo : public BITMAPINFO {
+    RGBQUAD fMoreSpaceForColors[1];
+};
+
+class HDCOffscreen {
+public:
+    HDCOffscreen() {
+        fFont = 0;
+        fDC = 0;
+        fBM = 0;
+        fBits = NULL;
+        fWidth = fHeight = 0;
+        fIsBW = false;
+        fColor = kInvalid_Color;
+    }
+
+    ~HDCOffscreen() {
+        if (fDC) {
+            DeleteDC(fDC);
+        }
+        if (fBM) {
+            DeleteObject(fBM);
+        }
+    }
+
+    void init(HFONT font, const XFORM& xform) {
+        fFont = font;
+        fXform = xform;
+    }
+
+    const void* draw(const SkGlyph&, bool isBW, SkGdiRGB fgColor,
+                     size_t* srcRBPtr);
+
+private:
+    HDC     fDC;
+    HBITMAP fBM;
+    HFONT   fFont;
+    XFORM   fXform;
+    void*   fBits;  // points into fBM
+    COLORREF fColor;
+    int     fWidth;
+    int     fHeight;
+    bool    fIsBW;
+
+    enum {
+        // will always trigger us to reset the color, since we
+        // should only store 0 or 0x00FFFFFF or gray (0x007F7F7F)
+        kInvalid_Color = 12345
+    };
+};
+
+const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
+                               SkGdiRGB fgColor, size_t* srcRBPtr) {
+    if (0 == fDC) {
+        fDC = CreateCompatibleDC(0);
+        if (0 == fDC) {
+            return NULL;
+        }
+        SetGraphicsMode(fDC, GM_ADVANCED);
+        SetBkMode(fDC, TRANSPARENT);
+        SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
+        SelectObject(fDC, fFont);
+        fColor = kInvalid_Color;
+    }
+
+    if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
+        DeleteObject(fBM);
+        fBM = 0;
+    }
+    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);
+
+    int biWidth = isBW ? alignTo32(fWidth) : fWidth;
+
+    if (0 == fBM) {
+        MyBitmapInfo info;
+        sk_bzero(&info, sizeof(info));
+        if (isBW) {
+            RGBQUAD blackQuad = { 0, 0, 0, 0 };
+            RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
+            info.bmiColors[0] = blackQuad;
+            info.bmiColors[1] = whiteQuad;
+        }
+        info.bmiHeader.biSize = sizeof(info.bmiHeader);
+        info.bmiHeader.biWidth = biWidth;
+        info.bmiHeader.biHeight = fHeight;
+        info.bmiHeader.biPlanes = 1;
+        info.bmiHeader.biBitCount = isBW ? 1 : 32;
+        info.bmiHeader.biCompression = BI_RGB;
+        if (isBW) {
+            info.bmiHeader.biClrUsed = 2;
+        }
+        fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
+        if (0 == fBM) {
+            return NULL;
+        }
+        SelectObject(fDC, fBM);
+    }
+
+    // erase
+    size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
+    size_t size = fHeight * srcRB;
+    unsigned bg = (0 == color) ? 0xFF : 0;
+    memset(fBits, bg, size);
+
+    XFORM xform = fXform;
+    xform.eDx = (float)-glyph.fLeft;
+    xform.eDy = (float)-glyph.fTop;
+    SetWorldTransform(fDC, &xform);
+
+    uint16_t glyphID = glyph.getGlyphID();
+    BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, reinterpret_cast<LPCWSTR>(&glyphID), 1, NULL);
+    GdiFlush();
+    if (0 == ret) {
+        return NULL;
+    }
+    *srcRBPtr = srcRB;
+    // offset to the start of the image
+    return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+class SkScalerContext_Windows : public SkScalerContext {
+public:
+    SkScalerContext_Windows(const SkDescriptor* desc);
+    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);
+
+private:
+    HDCOffscreen fOffscreen;
+    SkScalar     fScale;  // to get from canonical size to real size
+    MAT2         fMat22;
+    XFORM        fXform;
+    HDC          fDDC;
+    HFONT        fSavefont;
+    HFONT        fFont;
+    SCRIPT_CACHE fSC;
+    int          fGlyphCount;
+
+    HFONT        fHiResFont;
+    MAT2         fMat22Identity;
+    SkMatrix     fHiResMatrix;
+};
+
+static float mul2float(SkScalar a, SkScalar b) {
+    return SkScalarToFloat(SkScalarMul(a, b));
+}
+
+static FIXED float2FIXED(float x) {
+    return SkFixedToFIXED(SkFloatToFixed(x));
+}
+
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
+
+#define HIRES_TEXTSIZE  2048
+#define HIRES_SHIFT     11
+static inline SkFixed HiResToFixed(int value) {
+    return value << (16 - HIRES_SHIFT);
+}
+
+static bool needHiResMetrics(const SkScalar mat[2][2]) {
+    return mat[1][0] || mat[0][1];
+}
+
+static BYTE compute_quality(const SkScalerContext::Rec& rec) {
+    switch (rec.fMaskFormat) {
+        case SkMask::kBW_Format:
+            return NONANTIALIASED_QUALITY;
+        case SkMask::kLCD16_Format:
+        case SkMask::kLCD32_Format:
+            return CLEARTYPE_QUALITY;
+        default:
+            if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
+                return CLEARTYPE_QUALITY;
+            } else {
+                return ANTIALIASED_QUALITY;
+            }
+    }
+}
+
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
+        : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
+        , 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);
+
+    // Scaling by the DPI is inconsistent with how Skia draws elsewhere
+    //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
+    LOGFONT lf;
+    GetLogFontByID(fRec.fFontID, &lf);
+    lf.lfHeight = -gCanonicalTextSize;
+    lf.lfQuality = compute_quality(fRec);
+    fFont = CreateFontIndirect(&lf);
+
+    // if we're rotated, or want fractional widths, create a hires font
+    fHiResFont = 0;
+    if (needHiResMetrics(fRec.fPost2x2)) {
+        lf.lfHeight = -HIRES_TEXTSIZE;
+        fHiResFont = CreateFontIndirect(&lf);
+
+        fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1);
+        fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0);
+
+        // construct a matrix to go from HIRES logical units to our device units
+        fRec.getSingleMatrix(&fHiResMatrix);
+        SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE));
+        fHiResMatrix.preScale(scale, scale);
+    }
+    fSavefont = (HFONT)SelectObject(fDDC, fFont);
+
+    if (needToRenderWithSkia(fRec)) {
+        this->forceGenerateImageFromPath();
+    }
+
+    fOffscreen.init(fFont, fXform);
+}
+
+SkScalerContext_Windows::~SkScalerContext_Windows() {
+    if (fDDC) {
+        ::SelectObject(fDDC, fSavefont);
+        ::DeleteDC(fDDC);
+    }
+    if (fFont) {
+        ::DeleteObject(fFont);
+    }
+    if (fHiResFont) {
+        ::DeleteObject(fHiResFont);
+    }
+    if (fSC) {
+        ::ScriptFreeCache(&fSC);
+    }
+}
+
+unsigned SkScalerContext_Windows::generateGlyphCount() {
+    if (fGlyphCount < 0) {
+        fGlyphCount = calculateGlyphCount(fDDC);
+    }
+    return fGlyphCount;
+}
+
+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
+    uint16_t index = 0;
+    WCHAR c[2];
+    // TODO(ctguil): Support characters that generate more than one glyph.
+    if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
+        // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
+        SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0));
+    } else {
+        // Use uniscribe to detemine glyph index for non-BMP characters.
+        // Need to add extra item to SCRIPT_ITEM to work around a bug in older
+        // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
+        SCRIPT_ITEM si[2 + 1];
+        int items;
+        SkAssertResult(
+            SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
+
+        WORD log[2];
+        SCRIPT_VISATTR vsa;
+        int glyphs;
+        SkAssertResult(SUCCEEDED(ScriptShape(
+            fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
+    }
+    return index;
+}
+
+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
+    this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
+
+    SkASSERT(fDDC);
+
+    GLYPHMETRICS gm;
+    sk_bzero(&gm, sizeof(gm));
+
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+
+    // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
+    // BlackBlox; we need the bigger one in case we need the image.  fAdvance is the same.
+    uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+    if (GDI_ERROR == ret) {
+        ensure_typeface_accessible(fRec.fFontID);
+        ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+    }
+
+    if (GDI_ERROR != ret) {
+        if (ret == 0) {
+            // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
+            gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
+        }
+        glyph->fWidth   = gm.gmBlackBoxX;
+        glyph->fHeight  = gm.gmBlackBoxY;
+        glyph->fTop     = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
+        glyph->fLeft    = SkToS16(gm.gmptGlyphOrigin.x);
+        glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
+        glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
+
+        // we outset in all dimensions, since the image may bleed outside
+        // of the computed bounds returned by GetGlyphOutline.
+        // This was deduced by trial and error for small text (e.g. 8pt), so there
+        // maybe a more precise way to make this adjustment...
+        //
+        // This test shows us clipping the tops of some of the CJK fonts unless we
+        // increase the top of the box by 2, hence the height by 4. This seems to
+        // correspond to an embedded bitmap font, but not sure.
+        //     LayoutTests/fast/text/backslash-to-yen-sign-euc.html
+        //
+        if (glyph->fWidth) {    // don't outset an empty glyph
+            glyph->fWidth += 4;
+            glyph->fHeight += 4;
+            glyph->fTop -= 2;
+            glyph->fLeft -= 2;
+        }
+
+        if (fHiResFont) {
+            SelectObject(fDDC, fHiResFont);
+            sk_bzero(&gm, sizeof(gm));
+            ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
+            if (GDI_ERROR != ret) {
+                SkPoint advance;
+                fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
+                glyph->fAdvanceX = SkScalarToFixed(advance.fX);
+                glyph->fAdvanceY = SkScalarToFixed(advance.fY);
+            }
+            SelectObject(fDDC, fFont);
+        }
+    } else {
+        glyph->fWidth = 0;
+    }
+}
+
+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+// Note: This code was borrowed from generateLineHeight, which has a note
+// stating that it may be incorrect.
+    if (!(mx || my))
+      return;
+
+    SkASSERT(fDDC);
+
+    OUTLINETEXTMETRIC otm;
+
+    uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
+    if (GDI_ERROR == ret) {
+        ensure_typeface_accessible(fRec.fFontID);
+        ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
+    }
+    if (sizeof(otm) != ret) {
+      return;
+    }
+
+    if (mx) {
+        mx->fTop = -fScale * otm.otmTextMetrics.tmAscent;
+        mx->fAscent = -fScale * otm.otmAscent;
+        mx->fDescent = -fScale * otm.otmDescent;
+        mx->fBottom = fScale * otm.otmTextMetrics.tmDescent;
+        mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
+                                 + otm.otmTextMetrics.tmExternalLeading);
+    }
+
+    if (my) {
+        my->fTop = -fScale * otm.otmTextMetrics.tmAscent;
+        my->fAscent = -fScale * otm.otmAscent;
+        my->fDescent = -fScale * otm.otmDescent;
+        my->fBottom = fScale * otm.otmTextMetrics.tmDescent;
+        my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
+                                 + otm.otmTextMetrics.tmExternalLeading);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+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 = 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() {
+    static bool gInited;
+    static uint8_t gTable[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);
+        gInited = true;
+    }
+    return gTable;
+}
+
+#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;
+}
+
+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);
+}
+
+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);
+}
+
+// Is this GDI color neither black nor white? If so, we have to keep this
+// image as is, rather than smashing it down to a BW mask.
+//
+// returns int instead of bool, since we don't want/have to pay to convert
+// the zero/non-zero value into a bool
+static int is_not_black_or_white(SkGdiRGB c) {
+    // same as (but faster than)
+    //      c &= 0x00FFFFFF;
+    //      return 0 == c || 0x00FFFFFF == c;
+    return (c + (c & 1)) & 0x00FFFFFF;
+}
+
+static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, int srcRB) {
+    for (int y = 0; y < height; ++y) {
+        for (int x = 0; x < width; ++x) {
+            if (is_not_black_or_white(src[x])) {
+                return false;
+            }
+        }
+        src = SkTAddByteOffset(src, srcRB);
+    }
+    return true;
+}
+
+static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
+                      const SkGlyph& glyph, int32_t xorMask) {
+    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);
+
+    int byteCount = width >> 3;
+    int bitCount = width & 7;
+
+    // adjust srcRB to skip the values in our byteCount loop,
+    // since we increment src locally there
+    srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
+
+    for (int y = 0; y < glyph.fHeight; ++y) {
+        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);
+                dst[i] = byte;
+                src += 8;
+            }
+        }
+        if (bitCount > 0) {
+            unsigned byte = 0;
+            unsigned mask = 0x80;
+            for (int i = 0; i < bitCount; i++) {
+                byte |= (src[i] ^ xorMask) & mask;
+                mask >>= 1;
+            }
+            dst[byteCount] = byte;
+        }
+        src = SkTAddByteOffset(src, srcRB);
+        dst -= dstRB;
+    }
+}
+
+static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
+                      const SkGlyph& glyph, int32_t xorMask) {
+    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);
+        }
+        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) {
+    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);
+        }
+        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) {
+    const size_t dstRB = glyph.rowBytes();
+    const int width = glyph.fWidth;
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)((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);
+        }
+        src = SkTAddByteOffset(src, srcRB);
+        dst = (SkPMColor*)((char*)dst - dstRB);
+    }
+}
+
+static inline unsigned clamp255(unsigned x) {
+    SkASSERT(x <= 256);
+    return x - (x >> 8);
+}
+
+#define WHITE_LUMINANCE_LIMIT   0xA0
+#define BLACK_LUMINANCE_LIMIT   0x40
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
+    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);
+    if (NULL == bits) {
+        ensure_typeface_accessible(fRec.fFontID);
+        bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
+        if (NULL == bits) {
+            sk_bzero(glyph.fImage, glyph.computeImageSize());
+            return;
+        }
+    }
+
+    if (table) {
+        SkGdiRGB* addr = (SkGdiRGB*)bits;
+        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, srcRB);
+        }
+    }
+
+    int width = glyph.fWidth;
+    size_t dstRB = glyph.rowBytes();
+    if (isBW) {
+        const uint8_t* src = (const uint8_t*)bits;
+        uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
+        for (int y = 0; y < glyph.fHeight; y++) {
+            memcpy(dst, src, dstRB);
+            src += srcRB;
+            dst -= dstRB;
+        }
+    } else if (isAA) {
+        // 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);
+    } 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);
+            ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
+        } else {
+            if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+                rgb_to_lcd16(src, srcRB, glyph, rgbXOR);
+            } else {
+                SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
+                rgb_to_lcd32(src, srcRB, glyph, rgbXOR);
+            }
+        }
+    }
+}
+
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
+
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    SkASSERT(&glyph && path);
+    SkASSERT(fDDC);
+
+    path->reset();
+
+#if 0
+    char buf[1024];
+    sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+    OutputDebugString(buf);
+#endif
+
+    GLYPHMETRICS gm;
+    uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
+    if (GDI_ERROR == total_size) {
+        ensure_typeface_accessible(fRec.fFontID);
+        total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
+    }
+
+    if (GDI_ERROR != total_size) {
+
+        const uint8_t* cur_glyph = glyphbuf;
+        const uint8_t* end_glyph = glyphbuf + total_size;
+
+        while(cur_glyph < end_glyph) {
+            const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
+
+            const uint8_t* end_poly = cur_glyph + th->cb;
+            const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
+
+            path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
+
+            while(cur_poly < end_poly) {
+                const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
+
+                if (pc->wType == TT_PRIM_LINE) {
+                    for (uint16_t i = 0; i < pc->cpfx; i++) {
+                        path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
+                    }
+                }
+
+                if (pc->wType == TT_PRIM_QSPLINE) {
+                    for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
+                        POINTFX pnt_b = pc->apfx[u];    // B is always the current point
+                        POINTFX pnt_c = pc->apfx[u+1];
+
+                        if (u < pc->cpfx - 2) {          // If not on last spline, compute C
+                            pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
+                            pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
+                        }
+
+                        path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
+                    }
+                }
+                cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
+            }
+            cur_glyph += th->cb;
+            path->close();
+        }
+    }
+    else {
+        SkASSERT(false);
+    }
+    //char buf[1024];
+    //sprintf(buf, "generatePath: count:%d\n", count);
+    //OutputDebugString(buf);
+}
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
+    // Initialize the MAT2 structure to the identify transformation matrix.
+    static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
+                        SkScalarToFIXED(0), SkScalarToFIXED(1)};
+    int flags = GGO_METRICS | GGO_GLYPH_INDEX;
+    GLYPHMETRICS gm;
+    if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
+        return false;
+    }
+    SkASSERT(advance);
+    *advance = gm.gmCellIncX;
+    return true;
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+    LOGFONT lf;
+    GetLogFontByID(fontID, &lf);
+    SkAdvancedTypefaceMetrics* info = NULL;
+
+    HDC hdc = CreateCompatibleDC(NULL);
+    HFONT font = CreateFontIndirect(&lf);
+    HFONT savefont = (HFONT)SelectObject(hdc, font);
+    HFONT designFont = NULL;
+
+    const char stem_chars[] = {'i', 'I', '!', '1'};
+    int16_t min_width;
+    unsigned glyphCount;
+
+    // To request design units, create a logical font whose height is specified
+    // as unitsPerEm.
+    OUTLINETEXTMETRIC otm;
+    unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
+    if (0 == otmRet) {
+        ensure_typeface_accessible(fontID);
+        otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
+    }
+    if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
+        goto Error;
+    }
+    lf.lfHeight = -SkToS32(otm.otmEMSquare);
+    designFont = CreateFontIndirect(&lf);
+    SelectObject(hdc, designFont);
+    if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
+        goto Error;
+    }
+    glyphCount = calculateGlyphCount(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
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+        populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
+    }
+
+    if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
+        info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+    } else {
+        info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
+        info->fItalicAngle = 0;
+        info->fAscent = 0;
+        info->fDescent = 0;
+        info->fStemV = 0;
+        info->fCapHeight = 0;
+        info->fBBox = SkIRect::MakeEmpty();
+        return info;
+    }
+
+    // If this bit is clear the font is a fixed pitch font.
+    if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+    }
+    if (otm.otmTextMetrics.tmItalic) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+    }
+    // Setting symbolic style by default for now.
+    info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+    if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+    } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
+            info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+    }
+
+    // The main italic angle of the font, in tenths of a degree counterclockwise
+    // from vertical.
+    info->fItalicAngle = otm.otmItalicAngle / 10;
+    info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
+    info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
+    // TODO(ctguil): Use alternate cap height calculation.
+    // MSDN says otmsCapEmHeight is not support but it is returning a value on
+    // my Win7 box.
+    info->fCapHeight = otm.otmsCapEmHeight;
+    info->fBBox =
+        SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
+                          otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
+
+    // Figure out a good guess for StemV - Min width of i, I, !, 1.
+    // This probably isn't very good with an italic font.
+    min_width = SHRT_MAX;
+    info->fStemV = 0;
+    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 bit 1 is set, the font may not be embedded in a document.
+    // If bit 1 is clear, the font can be embedded.
+    // If bit 2 is set, the embedding is read-only.
+    if (otm.otmfsType & 0x1) {
+        info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+    } else if (perGlyphInfo &
+               SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+        if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
+            appendRange(&info->fGlyphWidths, 0);
+            info->fGlyphWidths->fAdvance.append(1, &min_width);
+            finishRange(info->fGlyphWidths.get(), 0,
+                        SkAdvancedTypefaceMetrics::WidthRange::kDefault);
+        } else {
+            info->fGlyphWidths.reset(
+                getAdvanceData(hdc,
+                               glyphCount,
+                               glyphIDs,
+                               glyphIDsCount,
+                               &getWidthAdvance));
+        }
+    }
+
+Error:
+    SelectObject(hdc, savefont);
+    DeleteObject(designFont);
+    DeleteObject(font);
+    DeleteDC(hdc);
+
+    return info;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+
+    //Should not be used on Windows, keep linker happy
+    SkASSERT(false);
+    return SkCreateTypefaceFromLOGFONT(get_default_font());
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+    const DWORD kTTCTag =
+        SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
+    LOGFONT lf;
+    GetLogFontByID(uniqueID, &lf);
+
+    HDC hdc = ::CreateCompatibleDC(NULL);
+    HFONT font = CreateFontIndirect(&lf);
+    HFONT savefont = (HFONT)SelectObject(hdc, font);
+
+    SkMemoryStream* stream = NULL;
+    DWORD tables[2] = {kTTCTag, 0};
+    for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
+        size_t bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
+        if (bufferSize == GDI_ERROR) {
+            ensure_typeface_accessible(uniqueID);
+            bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
+        }
+        if (bufferSize != GDI_ERROR) {
+            stream = new SkMemoryStream(bufferSize);
+            if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(),
+                            bufferSize)) {
+                break;
+            } else {
+                delete stream;
+                stream = NULL;
+            }
+        }
+    }
+
+    SelectObject(hdc, savefont);
+    DeleteObject(font);
+    DeleteDC(hdc);
+
+    return stream;
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    return SkNEW_ARGS(SkScalerContext_Windows, (desc));
+}
+
+/** 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[],
+                                       const void* data, size_t bytelength,
+                                       SkTypeface::Style style) {
+    LOGFONT lf;
+    if (NULL == familyFace && NULL == familyName) {
+        lf = get_default_font();
+    } else if (familyFace) {
+        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';
+    }
+    setStyle(&lf, style);
+    return SkCreateTypefaceFromLOGFONT(lf);
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    printf("SkFontHost::CreateTypefaceFromFile unimplemented");
+    return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+    unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
+                                  SkScalerContext::kAutohinting_Flag |
+                                  SkScalerContext::kEmbeddedBitmapText_Flag |
+                                  SkScalerContext::kEmbolden_Flag |
+                                  SkScalerContext::kSubpixelPositioning_Flag |
+                                  SkScalerContext::kLCD_BGROrder_Flag |
+                                  SkScalerContext::kLCD_Vertical_Flag;
+    rec->fFlags &= ~flagsWeDontSupport;
+
+    SkPaint::Hinting h = rec->getHinting();
+
+    // I think we can support no-hinting, if we get hires outlines and just
+    // use skia to rasterize into a gray-scale mask...
+#if 0
+    switch (h) {
+        case SkPaint::kNo_Hinting:
+        case SkPaint::kSlight_Hinting:
+            h = SkPaint::kNo_Hinting;
+            break;
+        case SkPaint::kNormal_Hinting:
+        case SkPaint::kFull_Hinting:
+            h = SkPaint::kNormal_Hinting;
+            break;
+        default:
+            SkDEBUGFAIL("unknown hinting");
+    }
+#else
+    h = SkPaint::kNormal_Hinting;
+#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
+    if (isLCD(*rec) && !isAxisAligned(*rec)) {
+        rec->fMaskFormat = SkMask::kA8_Format;
+    }
+#endif
+
+#if 0
+    if (SkMask::kLCD16_Format == rec->fMaskFormat) {
+        rec->fMaskFormat = SkMask::kLCD32_Format;
+    }
+#endif
+}
+
+#endif // WIN32
diff --git a/legacy/src/ports/SkGlobalInitialization_chromium.cpp b/legacy/src/ports/SkGlobalInitialization_chromium.cpp
new file mode 100644
index 0000000..6a7b213
--- /dev/null
+++ b/legacy/src/ports/SkGlobalInitialization_chromium.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapProcShader.h"
+#include "SkBlurImageFilter.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorFilter.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkLayerDrawLooper.h"
+#include "SkMallocPixelRef.h"
+#include "SkXfermode.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();
+}
diff --git a/legacy/src/ports/SkGlobalInitialization_default.cpp b/legacy/src/ports/SkGlobalInitialization_default.cpp
new file mode 100644
index 0000000..6be776a
--- /dev/null
+++ b/legacy/src/ports/SkGlobalInitialization_default.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "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"
+
+void SkFlattenable::InitializeFlattenables() {
+    SkBitmapProcShader::Init();
+    SkEffects::Init();
+    SkPathEffect::Init();
+    SkShape::Init();
+    SkXfermode::Init();
+}
+
+void SkPixelRef::InitializeFlattenables() {
+    SkFlipPixelRef::Init();
+    SkImageRef_GlobalPool::Init();
+    SkMallocPixelRef::Init();
+}
+
+#endif
diff --git a/legacy/src/ports/SkHarfBuzzFont.cpp b/legacy/src/ports/SkHarfBuzzFont.cpp
new file mode 100644
index 0000000..12f37f5
--- /dev/null
+++ b/legacy/src/ports/SkHarfBuzzFont.cpp
@@ -0,0 +1,188 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkHarfBuzzFont.h"
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+
+// HB_Fixed is a 26.6 fixed point format.
+static inline HB_Fixed SkScalarToHarfbuzzFixed(SkScalar value) {
+#ifdef SK_SCALAR_IS_FLOAT
+    return static_cast<HB_Fixed>(value * 64);
+#else
+    // convert .16 to .6
+    return value >> (16 - 6);
+#endif
+}
+
+static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters,
+                              hb_uint32 length, HB_Glyph* glyphs,
+                              hb_uint32* glyphsSize, HB_Bool isRTL) {
+    SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData);
+    SkPaint paint;
+
+    paint.setTypeface(font->getTypeface());
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t),
+                                       reinterpret_cast<uint16_t*>(glyphs));
+
+    // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
+    // |glyphs| array needs to be converted.
+    for (int i = numGlyphs - 1; i >= 0; --i) {
+        uint16_t value;
+        // We use a memcpy to avoid breaking strict aliasing rules.
+        memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(uint16_t));
+        glyphs[i] = value;
+    }
+
+    *glyphsSize = numGlyphs;
+    return 1;
+}
+
+static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs,
+                         hb_uint32 numGlyphs, HB_Fixed* advances, int flags) {
+    SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    SkAutoMalloc storage(numGlyphs * (sizeof(SkScalar) + sizeof(uint16_t)));
+    SkScalar* scalarWidths = reinterpret_cast<SkScalar*>(storage.get());
+    uint16_t* glyphs16 = reinterpret_cast<uint16_t*>(scalarWidths + numGlyphs);
+
+    // convert HB 32bit glyphs to skia's 16bit
+    for (hb_uint32 i = 0; i < numGlyphs; ++i) {
+        glyphs16[i] = SkToU16(glyphs[i]);
+    }
+    paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarWidths);
+
+    for (hb_uint32 i = 0; i < numGlyphs; ++i) {
+        advances[i] = SkScalarToHarfbuzzFixed(scalarWidths[i]);
+    }
+}
+
+static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters,
+                         hb_uint32 length) {
+    SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData);
+    SkPaint paint;
+
+    paint.setTypeface(font->getTypeface());
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    return paint.containsText(characters, length * sizeof(uint16_t));
+}
+
+static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags,
+                                hb_uint32 index, HB_Fixed* xPos, HB_Fixed* yPos,
+                                hb_uint32* resultingNumPoints) {
+    SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    if (flags & HB_ShaperFlag_UseDesignMetrics) {
+        paint.setHinting(SkPaint::kNo_Hinting);
+    }
+
+    SkPath path;
+    uint16_t glyph16 = SkToU16(glyph);
+    paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
+    int numPoints = path.countPoints();
+    if (index >= numPoints) {
+        return HB_Err_Invalid_SubTable;
+    }
+
+    SkPoint pt = path.getPoint(index);
+    *xPos = SkScalarToHarfbuzzFixed(pt.fX);
+    *yPos = SkScalarToHarfbuzzFixed(pt.fY);
+    *resultingNumPoints = numPoints;
+
+    return HB_Err_Ok;
+}
+
+static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph,
+                            HB_GlyphMetrics* metrics) {
+    SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    SkScalar width;
+    SkRect bounds;
+    uint16_t glyph16 = SkToU16(glyph);
+    paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
+
+    metrics->x = SkScalarToHarfbuzzFixed(bounds.fLeft);
+    metrics->y = SkScalarToHarfbuzzFixed(bounds.fTop);
+    metrics->width = SkScalarToHarfbuzzFixed(bounds.width());
+    metrics->height = SkScalarToHarfbuzzFixed(bounds.height());
+
+    metrics->xOffset = SkScalarToHarfbuzzFixed(width);
+    // We can't actually get the |y| correct because Skia doesn't export
+    // the vertical advance. However, nor we do ever render vertical text at
+    // the moment so it's unimportant.
+    metrics->yOffset = 0;
+}
+
+static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
+{
+    SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData);
+    SkPaint paint;
+    SkPaint::FontMetrics skiaMetrics;
+
+    font->setupPaint(&paint);
+    paint.getFontMetrics(&skiaMetrics);
+
+    switch (metric) {
+    case HB_FontAscent:
+        return SkScalarToHarfbuzzFixed(-skiaMetrics.fAscent);
+    default:
+        SkDebugf("--- unknown harfbuzz metric enum %d\n", metric);
+        return 0;
+    }
+}
+
+static HB_FontClass gSkHarfBuzzFontClass = {
+    stringToGlyphs,
+    glyphsToAdvances,
+    canRender,
+    getOutlinePoint,
+    getGlyphMetrics,
+    getFontMetric,
+};
+
+const HB_FontClass& SkHarfBuzzFont::GetFontClass() {
+    return gSkHarfBuzzFontClass;
+}
+
+HB_Error SkHarfBuzzFont::GetFontTableFunc(void* voidface, const HB_Tag tag,
+                                          HB_Byte* buffer, HB_UInt* len) {
+    SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(voidface);
+    uint32_t uniqueID = SkTypeface::UniqueID(font->getTypeface());
+
+    const size_t tableSize = SkFontHost::GetTableSize(uniqueID, tag);
+    if (!tableSize) {
+        return HB_Err_Invalid_Argument;
+    }
+    // If Harfbuzz specified a NULL buffer then it's asking for the size.
+    if (!buffer) {
+        *len = tableSize;
+        return HB_Err_Ok;
+    }
+
+    if (*len < tableSize) {
+        // is this right, or should we just copy less than the full table?
+        return HB_Err_Invalid_Argument;
+    }
+    SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer);
+    return HB_Err_Ok;
+}
+
diff --git a/legacy/src/ports/SkImageDecoder_CG.cpp b/legacy/src/ports/SkImageDecoder_CG.cpp
new file mode 100644
index 0000000..4e2bcc9
--- /dev/null
+++ b/legacy/src/ports/SkImageDecoder_CG.cpp
@@ -0,0 +1,189 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkMovie.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkCGUtils.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+static void malloc_release_proc(void* info, const void* data, size_t size) {
+    sk_free(info);
+}
+
+static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) {
+    // TODO: use callbacks, so we don't have to load all the data into RAM
+    size_t len = stream->getLength();
+    void* data = sk_malloc_throw(len);
+    stream->read(data, len);
+    
+    return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
+}
+
+static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) {
+    CGDataProviderRef data = SkStreamToDataProvider(stream);
+    CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0);
+    CGDataProviderRelease(data);
+    return imageSrc;
+}
+
+class SkImageDecoder_CG : public SkImageDecoder {
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+};
+
+#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
+
+bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+    CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
+
+    if (NULL == imageSrc) {
+        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);
+
+    // 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);
+    CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
+    CGContextRelease(cg);
+
+    bm->unlockPixels();
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+    return SkNEW(SkImageDecoder_CG);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static size_t consumer_put(void* info, const void* buffer, size_t count) {
+    SkWStream* stream = reinterpret_cast<SkWStream*>(info);
+    return stream->write(buffer, count) ? count : 0;
+}
+
+static void consumer_release(void* info) {
+    // we do nothing, since by design we don't "own" the stream (i.e. info)
+}
+
+static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
+    CGDataConsumerCallbacks procs;
+    procs.putBytes = consumer_put;
+    procs.releaseConsumer = consumer_release;
+    // we don't own/reference the stream, so it our consumer must not live
+    // longer that our caller's ownership of the stream
+    return CGDataConsumerCreate(stream, &procs);
+}
+
+static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
+                                                        CFStringRef type) {
+    CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
+    if (NULL == consumer) {
+        return NULL;
+    }
+    SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
+    
+    return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
+}
+
+class SkImageEncoder_CG : public SkImageEncoder {
+public:
+    SkImageEncoder_CG(Type t) : fType(t) {}
+
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+    
+private:
+    Type fType;
+};
+
+/*  Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes
+    to our SkWStream. Since we don't reference/own the SkWStream, our consumer
+    must only live for the duration of the onEncode() method.
+ */
+bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm,
+                                 int quality) {
+    CFStringRef type;
+    switch (fType) {
+        case kJPEG_Type:
+            type = kUTTypeJPEG;
+            break;
+        case kPNG_Type:
+            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);
+    if (NULL == image) {
+        return false;
+    }
+    SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image);
+
+    CGImageDestinationAddImage(dst, image, NULL);
+    return CGImageDestinationFinalize(dst);
+}
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+    switch (t) {
+        case kJPEG_Type:
+        case kPNG_Type:
+            break;
+        default:
+            return NULL;
+    }
+    return SkNEW_ARGS(SkImageEncoder_CG, (t));
+}
+
diff --git a/legacy/src/ports/SkImageDecoder_WIC.cpp b/legacy/src/ports/SkImageDecoder_WIC.cpp
new file mode 100644
index 0000000..a69ed4a
--- /dev/null
+++ b/legacy/src/ports/SkImageDecoder_WIC.cpp
@@ -0,0 +1,307 @@
+
+/*
+ * 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 <wincodec.h>
+#include "SkAutoCoInitialize.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkIStream.h"
+#include "SkMovie.h"
+#include "SkStream.h"
+#include "SkTScopedComPtr.h"
+
+class SkImageDecoder_WIC : public SkImageDecoder {
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+};
+
+bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+    //Initialize COM.
+    SkAutoCoInitialize scopedCo;
+    if (!scopedCo.succeeded()) {
+        return false;
+    }
+    
+    HRESULT hr = S_OK;
+    
+    //Create Windows Imaging Component ImagingFactory.
+    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
+    if (SUCCEEDED(hr)) {
+        hr = CoCreateInstance(
+            CLSID_WICImagingFactory
+            , NULL
+            , CLSCTX_INPROC_SERVER
+            , 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)) {
+        hr = piImagingFactory->CreateDecoderFromStream(
+            piStream.get()                    //Image to be decoded
+            , NULL                            //No particular vendor
+            , WICDecodeMetadataCacheOnDemand  //Cache metadata when needed
+            , &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)) {
+        hr = piBitmapFrameDecode->QueryInterface(
+            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);
+        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+            return true;
+        }
+        if (!this->allocPixelRef(bm, NULL)) {
+            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
+            , GUID_WICPixelFormat32bppPBGRA   //Destination pixel format
+            , WICBitmapDitherTypeNone         //Specified dither patterm
+            , NULL                            //Specify a particular palette
+            , 0.f                             //Alpha threshold
+            , WICBitmapPaletteTypeCustom      //Palette translation type
+        );
+    }
+    
+    //Get the BitmapSource interface of the format converter.
+    SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
+    if (SUCCEEDED(hr)) {
+        hr = piFormatConverter->QueryInterface(
+            IID_PPV_ARGS(&piBitmapSourceConverted)
+        );
+    }
+    
+    //Copy the pixels into the bitmap.
+    if (SUCCEEDED(hr)) {
+        SkAutoLockPixels alp(*bm);
+        bm->eraseColor(0);
+        const int stride = bm->rowBytes();
+        hr = piBitmapSourceConverted->CopyPixels(
+            NULL,                             //Get all the pixels
+            stride,
+            stride * height,
+            reinterpret_cast<BYTE *>(bm->getPixels())
+        );
+    }
+    
+    return SUCCEEDED(hr);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+    return SkNEW(SkImageDecoder_WIC);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+class SkImageEncoder_WIC : public SkImageEncoder {
+public:
+    SkImageEncoder_WIC(Type t) : fType(t) {}
+
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+
+private:
+    Type fType;
+};
+
+bool SkImageEncoder_WIC::onEncode(SkWStream* stream
+                                , const SkBitmap& bitmapOrig
+                                , int quality)
+{
+    GUID type;
+    switch (fType) {
+        case kJPEG_Type:
+            type = GUID_ContainerFormatJpeg;
+            break;
+        case kPNG_Type:
+            type = GUID_ContainerFormatPng;
+            break;
+        default:
+            return false;
+    }
+
+    //Convert to 8888 if needed.
+    const SkBitmap* bitmap;
+    SkBitmap bitmapCopy;
+    if (SkBitmap::kARGB_8888_Config == bitmapOrig.config()) {
+        bitmap = &bitmapOrig;
+    } else {
+        if (!bitmapOrig.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config)) {
+            return false;
+        }
+        bitmap = &bitmapCopy;
+    }
+
+    //Initialize COM.
+    SkAutoCoInitialize scopedCo;
+    if (!scopedCo.succeeded()) {
+        return false;
+    }
+    
+    HRESULT hr = S_OK;
+    
+    //Create Windows Imaging Component ImagingFactory.
+    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
+    if (SUCCEEDED(hr)) {
+        hr = CoCreateInstance(
+            CLSID_WICImagingFactory
+            , NULL
+            , CLSCTX_INPROC_SERVER
+            , 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,
+        //  write and set hr iff property exists.
+        piPropertybag->Write(1, &name, &value);
+    }
+    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;
+    if (SUCCEEDED(hr)) {
+        hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
+    }
+    if (SUCCEEDED(hr)) {
+        //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);
+        hr = piBitmapFrameEncode->WritePixels(
+            height
+            , bitmap->rowBytes()
+            , bitmap->rowBytes()*height
+            , reinterpret_cast<BYTE*>(bitmap->getPixels()));
+    }
+    
+    if (SUCCEEDED(hr)) {
+        hr = piBitmapFrameEncode->Commit();
+    }
+    
+    if (SUCCEEDED(hr)) {
+        hr = piEncoder->Commit();
+    }
+    
+    return SUCCEEDED(hr);
+}
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+    switch (t) {
+        case kJPEG_Type:
+        case kPNG_Type:
+            break;
+        default:
+            return NULL;
+    }
+    return SkNEW_ARGS(SkImageEncoder_WIC, (t));
+}
+
diff --git a/legacy/src/ports/SkImageDecoder_empty.cpp b/legacy/src/ports/SkImageDecoder_empty.cpp
new file mode 100644
index 0000000..e4079d0
--- /dev/null
+++ b/legacy/src/ports/SkImageDecoder_empty.cpp
@@ -0,0 +1,99 @@
+
+/*
+ * 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 "SkImageDecoder.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
+};
+
+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;
+        }
+    }
+    return false;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+typedef SkMovie* (*SkMovieFactoryProc)(SkStream*);
+
+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;
+    }
+}
+
diff --git a/legacy/src/ports/SkImageRef_ashmem.cpp b/legacy/src/ports/SkImageRef_ashmem.cpp
new file mode 100644
index 0000000..f9c6aff
--- /dev/null
+++ b/legacy/src/ports/SkImageRef_ashmem.cpp
@@ -0,0 +1,244 @@
+
+/*
+ * 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/legacy/src/ports/SkImageRef_ashmem.h
similarity index 100%
rename from src/ports/SkImageRef_ashmem.h
rename to legacy/src/ports/SkImageRef_ashmem.h
diff --git a/legacy/src/ports/SkMemory_brew.cpp b/legacy/src/ports/SkMemory_brew.cpp
new file mode 100644
index 0000000..96af702
--- /dev/null
+++ b/legacy/src/ports/SkMemory_brew.cpp
@@ -0,0 +1,55 @@
+/* libs/graphics/ports/SkMemory_brew.cpp
+ *
+ * Copyright 2009, The Android Open Source Project
+ * Copyright 2009, Company 100, Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_BREW
+
+#include <AEEStdLib.h>
+
+void sk_throw() {
+    SkDEBUGFAIL("sk_throw");
+    abort();
+}
+
+void sk_out_of_memory(void) {
+    SkDEBUGFAIL("sk_out_of_memory");
+    abort();
+}
+
+void* sk_malloc_throw(size_t size) {
+    return sk_malloc_flags(size, SK_MALLOC_THROW);
+}
+
+void* sk_realloc_throw(void* addr, size_t size) {
+    void* p = REALLOC(addr, size | ALLOC_NO_ZMEM);
+    if (size == 0) {
+        return p;
+    }
+    if (p == NULL) {
+        sk_throw();
+    }
+    return p;
+}
+
+void sk_free(void* p) {
+    FREEIF(p);
+}
+
+void* sk_malloc_flags(size_t size, unsigned flags) {
+    void* p = MALLOC(size | ALLOC_NO_ZMEM);
+    if (p == NULL) {
+        if (flags & SK_MALLOC_THROW) {
+            sk_throw();
+        }
+    }
+    return p;
+}
+
+#endif
diff --git a/legacy/src/ports/SkMemory_malloc.cpp b/legacy/src/ports/SkMemory_malloc.cpp
new file mode 100644
index 0000000..44e43ad
--- /dev/null
+++ b/legacy/src/ports/SkMemory_malloc.cpp
@@ -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.
+ */
+#include "SkTypes.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+void sk_throw() {
+    SkDEBUGFAIL("sk_throw");
+    abort();
+}
+
+void sk_out_of_memory(void) {
+    SkDEBUGFAIL("sk_out_of_memory");
+    abort();
+}
+
+void* sk_malloc_throw(size_t size) {
+    return sk_malloc_flags(size, SK_MALLOC_THROW);
+}
+
+void* sk_realloc_throw(void* addr, size_t size) {
+    void* p = realloc(addr, size);
+    if (size == 0) {
+        return p;
+    }
+    if (p == NULL) {
+        sk_throw();
+    }
+    return p;
+}
+
+void sk_free(void* p) {
+    if (p) {
+        free(p);
+    }
+}
+
+void* sk_malloc_flags(size_t size, unsigned flags) {
+    void* p = malloc(size);
+    if (p == NULL) {
+        if (flags & SK_MALLOC_THROW) {
+            sk_throw();
+        }
+    }
+    return p;
+}
+
diff --git a/legacy/src/ports/SkOSFile_brew.cpp b/legacy/src/ports/SkOSFile_brew.cpp
new file mode 100644
index 0000000..50e133f
--- /dev/null
+++ b/legacy/src/ports/SkOSFile_brew.cpp
@@ -0,0 +1,90 @@
+/* libs/graphics/ports/SkOSFile_brew.cpp
+ *
+ * Copyright 2006, The Android Open Source Project
+ * Copyright 2009, Company 100, Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkOSFile.h"
+
+#ifdef SK_BUILD_FOR_BREW
+
+#include <AEEAppGen.h>
+#include <AEEFile.h>
+#include <AEEStdLib.h>
+
+SkFILE* sk_fopen(const char path[], SkFILE_Flags flags)
+{
+    int err;
+    OpenFileMode mode;
+    IFileMgr* fileMgr;
+    IFile* file;
+    IShell* shell;
+
+    shell = reinterpret_cast<AEEApplet*>(GETAPPINSTANCE())->m_pIShell;
+    err = ISHELL_CreateInstance(shell, AEECLSID_FILEMGR, (void**)&fileMgr);
+    if (err!= SUCCESS)
+        return NULL;
+
+    if (flags & kWrite_SkFILE_Flag)
+        mode = _OFM_READWRITE;
+    else /* kRead_SkFILE_Flag */
+        mode = _OFM_READ;
+
+    file = IFILEMGR_OpenFile(fileMgr, path, mode);
+    IFILEMGR_Release(fileMgr);
+
+    return (SkFILE*)file;
+}
+
+size_t sk_fgetsize(SkFILE* f)
+{
+    FileInfo fileInfo;
+
+    IFILE_GetInfo((IFile*)f, &fileInfo);
+    return fileInfo.dwSize;
+}
+
+bool sk_frewind(SkFILE* f)
+{
+    SkASSERT(f);
+    return IFILE_Seek((IFile*)f,  _SEEK_START, 0) == SUCCESS;
+}
+
+size_t sk_fread(void* buffer, size_t byteCount, SkFILE* f)
+{
+    SkASSERT(f);
+    if (buffer == NULL)
+    {
+        int err = IFILE_Seek((IFile*)f, _SEEK_CURRENT, (int)byteCount);
+        if (err == EFAILED) {
+            SkDEBUGF(("sk_fread: IFILE_Seek(%d) failed returned:%d\n", byteCount, err));
+            return 0;
+        }
+        return byteCount;
+    }
+    else
+        return IFILE_Read((IFile*)f, buffer, byteCount);
+}
+
+size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f)
+{
+    SkASSERT(f);
+    return IFILE_Write((IFile*)f, buffer, byteCount);
+}
+
+void sk_fflush(SkFILE* f)
+{
+    SkASSERT(f);
+}
+
+void sk_fclose(SkFILE* f)
+{
+    SkASSERT(f);
+    IFILE_Release((IFile*)f);
+}
+
+#endif
diff --git a/legacy/src/ports/SkOSFile_stdio.cpp b/legacy/src/ports/SkOSFile_stdio.cpp
new file mode 100644
index 0000000..764b466
--- /dev/null
+++ b/legacy/src/ports/SkOSFile_stdio.cpp
@@ -0,0 +1,94 @@
+
+/*
+ * 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 "SkOSFile.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+SkFILE* sk_fopen(const char path[], SkFILE_Flags flags)
+{
+    char    perm[4];
+    char*   p = perm;
+
+    if (flags & kRead_SkFILE_Flag)
+        *p++ = 'r';
+    if (flags & kWrite_SkFILE_Flag)
+        *p++ = 'w';
+    *p++ = 'b';
+    *p = 0;
+
+    SkFILE* f = (SkFILE*)::fopen(path, perm);
+#if 0
+    if (NULL == f)
+        SkDebugf("sk_fopen failed for %s (%s), errno=%s\n", path, perm, strerror(errno));
+#endif
+    return f;
+}
+
+size_t sk_fgetsize(SkFILE* f)
+{
+    SkASSERT(f);
+
+    size_t  curr = ::ftell((FILE*)f);       // remember where we are
+    ::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
+    return size;
+}
+
+bool sk_frewind(SkFILE* f)
+{
+    SkASSERT(f);
+    ::rewind((FILE*)f);
+//  ::fseek((FILE*)f, 0, SEEK_SET);
+    return true;
+}
+
+size_t sk_fread(void* buffer, size_t byteCount, SkFILE* f)
+{
+    SkASSERT(f);
+    if (buffer == NULL)
+    {
+        size_t curr = ::ftell((FILE*)f);
+        if ((long)curr == -1) {
+            SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof((FILE*)f), ferror((FILE*)f)));
+            return 0;
+        }
+    //  ::fseek((FILE*)f, (long)(curr + byteCount), SEEK_SET);
+        int err = ::fseek((FILE*)f, (long)byteCount, SEEK_CUR);
+        if (err != 0) {
+            SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n",
+                        byteCount, curr, feof((FILE*)f), ferror((FILE*)f), err));
+            return 0;
+        }
+        return byteCount;
+    }
+    else
+        return ::fread(buffer, 1, byteCount, (FILE*)f);
+}
+
+size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f)
+{
+    SkASSERT(f);
+    return ::fwrite(buffer, 1, byteCount, (FILE*)f);
+}
+
+void sk_fflush(SkFILE* f)
+{
+    SkASSERT(f);
+    ::fflush((FILE*)f);
+}
+
+void sk_fclose(SkFILE* f)
+{
+    SkASSERT(f);
+    ::fclose((FILE*)f);
+}
+
diff --git a/legacy/src/ports/SkThread_none.cpp b/legacy/src/ports/SkThread_none.cpp
new file mode 100644
index 0000000..8361021
--- /dev/null
+++ b/legacy/src/ports/SkThread_none.cpp
@@ -0,0 +1,31 @@
+
+/*
+ * 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 "SkThread.h"
+
+int32_t sk_atomic_inc(int32_t* addr) {
+    int32_t value = *addr;
+    *addr = value + 1;
+    return value;
+}
+
+int32_t sk_atomic_dec(int32_t* addr) {
+    int32_t value = *addr;
+    *addr = value - 1;
+    return value;
+}
+
+SkMutex::SkMutex() {}
+
+SkMutex::~SkMutex() {}
+
+void SkMutex::acquire() {}
+
+void SkMutex::release() {}
+
diff --git a/legacy/src/ports/SkThread_pthread.cpp b/legacy/src/ports/SkThread_pthread.cpp
new file mode 100644
index 0000000..4750d4f
--- /dev/null
+++ b/legacy/src/ports/SkThread_pthread.cpp
@@ -0,0 +1,151 @@
+
+/*
+ * 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 "SkThread.h"
+
+#include <pthread.h>
+#include <errno.h>
+
+#ifndef SK_BUILD_FOR_ANDROID
+
+/**
+ We prefer the GCC intrinsic implementation of the atomic operations over the
+ 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
+ According to this the intrinsics are supported on ARM in LLVM 2.7+
+ http://llvm.org/releases/2.7/docs/ReleaseNotes.html
+*/
+#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __GNUC__ > 4
+    #if (defined(__x86_64) || defined(__i386__))
+        #define GCC_INTRINSIC
+    #endif
+#endif
+
+#if defined(GCC_INTRINSIC)
+
+int32_t sk_atomic_inc(int32_t* addr)
+{
+    return __sync_fetch_and_add(addr, 1);
+}
+
+int32_t sk_atomic_dec(int32_t* addr)
+{
+    return __sync_fetch_and_add(addr, -1);
+}
+
+#else
+
+SkMutex gAtomicMutex;
+
+int32_t sk_atomic_inc(int32_t* addr)
+{
+    SkAutoMutexAcquire ac(gAtomicMutex);
+
+    int32_t value = *addr;
+    *addr = value + 1;
+    return value;
+}
+
+int32_t sk_atomic_dec(int32_t* addr)
+{
+    SkAutoMutexAcquire ac(gAtomicMutex);
+
+    int32_t value = *addr;
+    *addr = value - 1;
+    return value;
+}
+
+#endif
+
+#endif // SK_BUILD_FOR_ANDROID
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void print_pthread_error(int status) {
+    switch (status) {
+    case 0: // success
+        break;
+    case EINVAL:
+        SkDebugf("pthread error [%d] EINVAL\n", status);
+        break;
+    case EBUSY:
+        SkDebugf("pthread error [%d] EBUSY\n", status);
+        break;
+    default:
+        SkDebugf("pthread error [%d] unknown\n", status);
+        break;
+    }
+}
+
+#ifdef SK_USE_POSIX_THREADS
+
+SkMutex::SkMutex() {
+    int status;
+
+    status = pthread_mutex_init(&fMutex, NULL);
+    if (status != 0) {
+        print_pthread_error(status);
+        SkASSERT(0 == status);
+    }
+}
+
+SkMutex::~SkMutex() {
+    int status = pthread_mutex_destroy(&fMutex);
+
+    // only report errors on non-global mutexes
+    if (status != 0) {
+        print_pthread_error(status);
+        SkASSERT(0 == status);
+    }
+}
+
+#else // !SK_USE_POSIX_THREADS
+
+SkMutex::SkMutex() {
+    if (sizeof(pthread_mutex_t) > sizeof(fStorage)) {
+        SkDEBUGF(("pthread mutex size = %d\n", sizeof(pthread_mutex_t)));
+        SkDEBUGFAIL("mutex storage is too small");
+    }
+
+    int status;
+    pthread_mutexattr_t attr;
+
+    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);
+}
+
+SkMutex::~SkMutex() {
+    int status = pthread_mutex_destroy((pthread_mutex_t*)fStorage);
+#if 0
+    // only report errors on non-global mutexes
+    if (!fIsGlobal) {
+        print_pthread_error(status);
+        SkASSERT(0 == status);
+    }
+#endif
+}
+
+void SkMutex::acquire() {
+    int status = pthread_mutex_lock((pthread_mutex_t*)fStorage);
+    print_pthread_error(status);
+    SkASSERT(0 == status);
+}
+
+void SkMutex::release() {
+    int status = pthread_mutex_unlock((pthread_mutex_t*)fStorage);
+    print_pthread_error(status);
+    SkASSERT(0 == status);
+}
+
+#endif // !SK_USE_POSIX_THREADS
diff --git a/legacy/src/ports/SkThread_win.cpp b/legacy/src/ports/SkThread_win.cpp
new file mode 100644
index 0000000..70b8e11
--- /dev/null
+++ b/legacy/src/ports/SkThread_win.cpp
@@ -0,0 +1,46 @@
+
+/*
+ * 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.
+ */
+
+
+#include <windows.h>
+#include <intrin.h>
+#include "SkThread.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)
+
+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_dec(int32_t* addr) {
+    return _InterlockedDecrement(reinterpret_cast<LONG*>(addr)) + 1;
+}
+
+SkMutex::SkMutex() {
+    SK_COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION),
+                      NotEnoughSizeForCriticalSection);
+    InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
+SkMutex::~SkMutex() {
+    DeleteCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
+void SkMutex::acquire() {
+    EnterCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
+void SkMutex::release() {
+    LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
diff --git a/legacy/src/ports/SkTime_Unix.cpp b/legacy/src/ports/SkTime_Unix.cpp
new file mode 100644
index 0000000..f519a69
--- /dev/null
+++ b/legacy/src/ports/SkTime_Unix.cpp
@@ -0,0 +1,39 @@
+
+/*
+ * 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 "SkTime.h"
+
+#include <sys/time.h>
+#include <time.h>
+
+void SkTime::GetDateTime(DateTime* dt)
+{
+    if (dt)
+    {
+        time_t m_time;
+        time(&m_time);
+        struct tm* tstruct;
+        tstruct = localtime(&m_time);
+
+        dt->fYear       = tstruct->tm_year;
+        dt->fMonth      = SkToU8(tstruct->tm_mon + 1);
+        dt->fDayOfWeek  = SkToU8(tstruct->tm_wday);
+        dt->fDay        = SkToU8(tstruct->tm_mday);
+        dt->fHour       = SkToU8(tstruct->tm_hour);
+        dt->fMinute     = SkToU8(tstruct->tm_min);
+        dt->fSecond     = SkToU8(tstruct->tm_sec);
+    }
+}
+
+SkMSec SkTime::GetMSecs()
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return (SkMSec) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds
+}
diff --git a/legacy/src/ports/SkTime_win.cpp b/legacy/src/ports/SkTime_win.cpp
new file mode 100644
index 0000000..37af9f2
--- /dev/null
+++ b/legacy/src/ports/SkTime_win.cpp
@@ -0,0 +1,38 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkTime.h"
+
+void SkTime::GetDateTime(DateTime* dt)
+{
+    if (dt)
+    {
+        SYSTEMTIME      st;
+        GetSystemTime(&st);
+
+        dt->fYear       = st.wYear;
+        dt->fMonth      = SkToU8(st.wMonth + 1);
+        dt->fDayOfWeek  = SkToU8(st.wDayOfWeek);
+        dt->fDay        = SkToU8(st.wDay);
+        dt->fHour       = SkToU8(st.wHour);
+        dt->fMinute     = SkToU8(st.wMinute);
+        dt->fSecond     = SkToU8(st.wSecond);
+    }
+}
+
+SkMSec SkTime::GetMSecs()
+{
+    FILETIME        ft;
+    LARGE_INTEGER   li;
+    GetSystemTimeAsFileTime(&ft);
+    li.LowPart  = ft.dwLowDateTime;
+    li.HighPart = ft.dwHighDateTime;
+    __int64 t  = li.QuadPart;       /* In 100-nanosecond intervals */
+    return (SkMSec)(t / 10000);               /* In milliseconds */
+}
diff --git a/legacy/src/ports/SkXMLParser_empty.cpp b/legacy/src/ports/SkXMLParser_empty.cpp
new file mode 100644
index 0000000..09b222e
--- /dev/null
+++ b/legacy/src/ports/SkXMLParser_empty.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 "SkXMLParser.h"
+
+bool SkXMLParser::parse(SkStream& docStream)
+{
+    return false;
+}
+
+bool SkXMLParser::parse(const char doc[], size_t len)
+{
+    return false;
+}
+
+void SkXMLParser::GetNativeErrorString(int error, SkString* str)
+{
+
+}
diff --git a/legacy/src/ports/SkXMLParser_expat.cpp b/legacy/src/ports/SkXMLParser_expat.cpp
new file mode 100644
index 0000000..c78dc35
--- /dev/null
+++ b/legacy/src/ports/SkXMLParser_expat.cpp
@@ -0,0 +1,141 @@
+
+/*
+ * 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 "SkXMLParser.h"
+#include "SkString.h"
+#include "SkStream.h"
+
+#include "expat.h"
+
+#ifdef SK_BUILD_FOR_PPI
+#define CHAR_16_TO_9
+#endif
+
+#if defined CHAR_16_TO_9
+inline size_t sk_wcslen(const short* char16) {
+    const short* start = char16;
+    while (*char16)
+        char16++;
+    return char16 - start;
+}
+
+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++) 
+        ch8[index] = (char) ch16[index];
+    ch8[index] = '\0';
+    return ch8;
+}
+#endif
+
+static void XMLCALL start_proc(void *data, const char *el, const char **attr)
+{
+#if defined CHAR_16_TO_9
+    size_t len = sk_wcslen((const short*) el);
+    SkAutoMalloc    el8(len + 1);
+    el = ConvertUnicodeToChar((const short*) el, len, el8);
+#endif
+    if (((SkXMLParser*)data)->startElement(el)) {
+        XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+        return;
+    }
+    while (*attr)
+    {
+        const char* attr0 = attr[0];
+        const char* attr1 = attr[1];
+#if defined CHAR_16_TO_9
+        size_t len0 = sk_wcslen((const short*) attr0);
+        SkAutoMalloc    attr0_8(len0 + 1);
+        attr0 = ConvertUnicodeToChar((const short*) attr0, len0, attr0_8);
+        size_t len1 = sk_wcslen((const short*) attr1);
+        SkAutoMalloc    attr1_8(len1 + 1);
+        attr1 = ConvertUnicodeToChar((const short*) attr1, len1, attr1_8);
+#endif
+        if (((SkXMLParser*)data)->addAttribute(attr0, attr1)) {
+            XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+            return;
+        }
+        attr += 2;
+    }
+}
+
+static void XMLCALL end_proc(void *data, const char *el)
+{
+#if defined CHAR_16_TO_9
+    size_t len = sk_wcslen((const short*) el);
+    SkAutoMalloc    el8(len + 1);
+    el = ConvertUnicodeToChar((const short*) el, len, el8);
+#endif
+    if (((SkXMLParser*)data)->endElement(el))
+        XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+}
+
+static void XMLCALL text_proc(void* data, const char* text, int len)
+{
+#if defined CHAR_16_TO_9
+    SkAutoMalloc    text8(len + 1);
+    text = ConvertUnicodeToChar((const short*) text, len, text8);
+#endif
+    if (((SkXMLParser*)data)->text(text, len))
+        XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+}
+
+bool SkXMLParser::parse(const char doc[], size_t len)
+{
+    if (len == 0) {
+        fError->fCode = SkXMLParserError::kEmptyFile;
+        reportError(NULL);
+        return false;
+    }
+    XML_Parser p = XML_ParserCreate(NULL);
+    SkASSERT(p);
+    fParser = p;
+    XML_SetElementHandler(p, start_proc, end_proc);
+    XML_SetCharacterDataHandler(p, text_proc);
+    XML_SetUserData(p, this);
+
+    bool success = true;
+    int error = XML_Parse(p, doc, len, true);
+    if (error == XML_STATUS_ERROR) {
+        reportError(p);
+        success = false;
+    }
+    XML_ParserFree(p);
+    return success;
+}
+
+bool SkXMLParser::parse(SkStream& input)
+{
+    size_t          len = input.read(NULL, 0);
+    SkAutoMalloc    am(len);
+    char*           doc = (char*)am.get();
+
+    input.rewind();
+    size_t  len2 = input.read(doc, len);
+    SkASSERT(len2 == len);
+
+    return this->parse(doc, len2);
+}
+
+void SkXMLParser::reportError(void* p)
+{
+    XML_Parser parser = (XML_Parser) p;
+    if (fError && parser) {
+        fError->fNativeCode = XML_GetErrorCode(parser);
+        fError->fLineNumber = XML_GetCurrentLineNumber(parser);
+    }
+}
+
+void SkXMLParser::GetNativeErrorString(int error, SkString* str)
+{
+    if (str)
+        str->set(XML_ErrorString((XML_Error) error));
+}
+
diff --git a/legacy/src/ports/SkXMLParser_tinyxml.cpp b/legacy/src/ports/SkXMLParser_tinyxml.cpp
new file mode 100644
index 0000000..2e308aa
--- /dev/null
+++ b/legacy/src/ports/SkXMLParser_tinyxml.cpp
@@ -0,0 +1,88 @@
+
+/*
+ * 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 "SkXMLParser.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "tinyxml.h"
+
+static void walk_elem(SkXMLParser* parser, const TiXmlElement* elem)
+{
+    //printf("walk_elem(%s) ", elem->Value());
+
+    parser->startElement(elem->Value());
+
+    const TiXmlAttribute* attr = elem->FirstAttribute();
+    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)
+    {
+        if (node->ToElement())
+            walk_elem(parser, node->ToElement());
+        else if (node->ToText())
+            parser->text(node->Value(), strlen(node->Value()));
+        node = node->NextSibling();
+    }
+    
+    parser->endElement(elem->Value());
+}
+
+static bool load_buf(SkXMLParser* parser, const char buf[])
+{
+    TiXmlDocument                   doc;
+
+    (void)doc.Parse(buf);
+    if (doc.Error())
+    {
+        printf("tinyxml error: <%s> row[%d] col[%d]\n", doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
+        return false;
+    }
+    
+    walk_elem(parser, doc.RootElement());
+    return true;
+}
+
+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);
+}
+
+bool SkXMLParser::parse(const char doc[], size_t len)
+{
+    SkAutoMalloc    buffer(len + 1);
+    char*           buf = (char*)buffer.get();
+    
+    memcpy(buf, doc, len);
+    buf[len] = 0;
+    
+    return load_buf(this, buf);
+}
+
+void SkXMLParser::GetNativeErrorString(int error, SkString* str)
+{
+    if (str)
+        str->set("GetNativeErrorString not implemented for TinyXml");
+}
+
diff --git a/legacy/src/ports/SkXMLPullParser_expat.cpp b/legacy/src/ports/SkXMLPullParser_expat.cpp
new file mode 100644
index 0000000..06cdc65
--- /dev/null
+++ b/legacy/src/ports/SkXMLPullParser_expat.cpp
@@ -0,0 +1,214 @@
+
+/*
+ * 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 "SkXMLParser.h"
+#include "SkChunkAlloc.h"
+#include "SkString.h"
+#include "SkStream.h"
+
+#include "expat.h"
+
+static inline char* dupstr(SkChunkAlloc& chunk, const char src[], size_t len)
+{
+    SkASSERT(src);
+    char*   dst = (char*)chunk.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+    
+    memcpy(dst, src, len);    
+    dst[len] = 0;
+    return dst;
+}
+
+static inline int count_pairs(const char** p)
+{
+    const char** start = p;
+    while (*p)
+    {
+        SkASSERT(p[1] != NULL);
+        p += 2;
+    }
+    return (p - start) >> 1;
+}
+
+struct Data {
+    Data() : fAlloc(2048), fState(NORMAL) {}
+    
+    XML_Parser              fParser;
+    SkXMLPullParser::Curr*  fCurr;
+    SkChunkAlloc            fAlloc;
+    
+    enum State {
+        NORMAL,
+        MISSED_START_TAG,
+        RETURN_END_TAG
+    };
+    State fState;
+    const char* fEndTag;    // if state is RETURN_END_TAG
+};
+
+static void XMLCALL start_proc(void *data, const char *el, const char **attr)
+{
+    Data*                   p = (Data*)data;
+    SkXMLPullParser::Curr*  c = p->fCurr;
+    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);
+    c->fAttrInfoCount = n;
+    c->fAttrInfos = info;
+
+    for (int i = 0; i < n; i++)
+    {
+        info[i].fName = dupstr(alloc, attr[0], strlen(attr[0]));
+        info[i].fValue = dupstr(alloc, attr[1], strlen(attr[1]));
+        attr += 2;
+    }
+
+    c->fEventType = SkXMLPullParser::START_TAG;
+    XML_StopParser(p->fParser, true);
+}
+
+static void XMLCALL end_proc(void *data, const char *el)
+{
+    Data*                   p = (Data*)data;
+    SkXMLPullParser::Curr*  c = p->fCurr;
+
+    if (c->fEventType == SkXMLPullParser::START_TAG)
+    {
+        /*  if we get here, we were called with a start_tag immediately
+            followed by this end_tag. The caller will only see the end_tag,
+            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);
+    }
+    else
+        c->fName = dupstr(p->fAlloc, el, strlen(el));
+
+    c->fEventType = SkXMLPullParser::END_TAG;
+    XML_StopParser(p->fParser, true);
+}
+
+#include <ctype.h>
+
+static bool isws(const char s[])
+{
+    for (; *s; s++)
+        if (!isspace(*s))
+            return false;
+    return true;
+}
+
+static void XMLCALL text_proc(void* data, const char* text, int len)
+{
+    Data*                   p = (Data*)data;
+    SkXMLPullParser::Curr*  c = p->fCurr;
+
+    c->fName = dupstr(p->fAlloc, text, len);
+    c->fIsWhitespace = isws(c->fName);
+
+    c->fEventType = SkXMLPullParser::TEXT;
+    XML_StopParser(p->fParser, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+struct SkXMLPullParser::Impl {
+    Data    fData;
+    void*   fBuffer;
+    size_t  fBufferLen;
+};
+
+static void reportError(XML_Parser parser)
+{
+    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);
+}
+
+bool SkXMLPullParser::onInit()
+{
+    fImpl = new Impl;
+
+    XML_Parser p = XML_ParserCreate(NULL);
+    SkASSERT(p);
+
+    fImpl->fData.fParser = p;
+    fImpl->fData.fCurr = &fCurr;
+
+    XML_SetElementHandler(p, start_proc, end_proc);
+    XML_SetCharacterDataHandler(p, text_proc);
+    XML_SetUserData(p, &fImpl->fData);
+
+    size_t len = fStream->read(NULL, 0);
+    fImpl->fBufferLen = len;
+    fImpl->fBuffer = sk_malloc_throw(len);
+    fStream->rewind();
+    size_t  len2 = fStream->read(fImpl->fBuffer, len);
+    return len2 == len;
+}
+
+void SkXMLPullParser::onExit()
+{
+    sk_free(fImpl->fBuffer);
+    XML_ParserFree(fImpl->fData.fParser);
+    delete fImpl;
+    fImpl = NULL;
+}
+
+SkXMLPullParser::EventType SkXMLPullParser::onNextToken()
+{
+    if (Data::RETURN_END_TAG == fImpl->fData.fState)
+    {
+        fImpl->fData.fState = Data::NORMAL;
+        fCurr.fName = fImpl->fData.fEndTag; // restore name from (below) save
+        return SkXMLPullParser::END_TAG;
+    }
+
+    fImpl->fData.fAlloc.reuse();
+
+    XML_Parser p = fImpl->fData.fParser;
+    XML_Status status;
+
+    status = XML_ResumeParser(p);
+    
+CHECK_STATUS:
+    switch (status) {
+    case XML_STATUS_OK:
+        return SkXMLPullParser::END_DOCUMENT;
+
+    case XML_STATUS_ERROR:
+        if (XML_GetErrorCode(p) != XML_ERROR_NOT_SUSPENDED)
+        {
+            reportError(p);
+            return SkXMLPullParser::ERROR;
+        }
+        status = XML_Parse(p, (const char*)fImpl->fBuffer, fImpl->fBufferLen, true);
+        goto CHECK_STATUS;
+
+    case XML_STATUS_SUSPENDED:
+        if (Data::MISSED_START_TAG == fImpl->fData.fState)
+        {
+            // 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            
+            return SkXMLPullParser::START_TAG;
+        }
+        break;
+    }
+    return fCurr.fEventType;
+}
+
diff --git a/src/ports/sk_predefined_gamma.h b/legacy/src/ports/sk_predefined_gamma.h
similarity index 100%
rename from src/ports/sk_predefined_gamma.h
rename to legacy/src/ports/sk_predefined_gamma.h
diff --git a/legacy/src/text/SkTextLayout.cpp b/legacy/src/text/SkTextLayout.cpp
new file mode 100644
index 0000000..fda4b2f
--- /dev/null
+++ b/legacy/src/text/SkTextLayout.cpp
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkTextLayout.h"
+
+SkTextStyle::SkTextStyle() {
+    fPaint.setAntiAlias(true);
+}
+
+SkTextStyle::SkTextStyle(const SkTextStyle& src) : fPaint(src.fPaint) {}
+
+SkTextStyle::SkTextStyle(const SkPaint& paint) : fPaint(paint) {}
+
+SkTextStyle::~SkTextStyle() {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTextLayout::SkTextLayout() {
+    fBounds.setEmpty();
+    fDefaultStyle = new SkTextStyle;
+}
+
+SkTextLayout::~SkTextLayout() {
+    fDefaultStyle->unref();
+    fLines.deleteAll();
+}
+
+void SkTextLayout::setText(const char text[], size_t length) {
+    fText.setCount(length);
+    memcpy(fText.begin(), text, length);
+}
+
+void SkTextLayout::setBounds(const SkRect& bounds) {
+    fBounds = bounds;
+    // if width changed, inval cache
+}
+
+SkTextStyle* SkTextLayout::setDefaultStyle(SkTextStyle* style) {
+    SkRefCnt_SafeAssign(fDefaultStyle, style);
+    return style;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkTextLayout::GlyphRun {
+    GlyphRun();
+    ~GlyphRun();
+
+    SkPoint*    fLocs;
+    uint16_t*   fGlyphIDs;
+    int         fCount;
+};
+
+SkTextLayout::GlyphRun::GlyphRun() : fLocs(NULL), fGlyphIDs(NULL), fCount(0) {}
+
+SkTextLayout::GlyphRun::~GlyphRun() {
+    delete[] fLocs;
+    delete[] fGlyphIDs;
+}
+
+struct SkTextLayout::Line {
+    Line() {}
+    ~Line();
+
+    SkScalar                fBaselineY;
+    SkTDArray<GlyphRun*>    fRuns;
+};
+
+SkTextLayout::Line::~Line() {
+    fRuns.deleteAll();
+}
+
+void SkTextLayout::draw(SkCanvas* canvas) {
+}
+
diff --git a/legacy/src/utils/SkBase64.cpp b/legacy/src/utils/SkBase64.cpp
new file mode 100644
index 0000000..a8d4e87
--- /dev/null
+++ b/legacy/src/utils/SkBase64.cpp
@@ -0,0 +1,187 @@
+
+/*
+ * 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 "SkBase64.h"
+
+#define DecodePad -2
+#define EncodePad 64
+
+static const char default_encode[] = 
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "0123456789+/=";
+
+static const signed char decodeData[] = {
+    62, -1, -1, -1, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, DecodePad, -1, -1,
+    -1,  0,  1,  2,  3,  4,  5,  6, 7,  8,  9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+SkBase64::SkBase64() : fLength((size_t) -1), fData(NULL) {
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable 'two', etc. may be used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+SkBase64::Error SkBase64::decode(const void* srcPtr, size_t size, bool writeDestination) {
+    unsigned char* dst = (unsigned char*) fData;
+    const unsigned char* dstStart = (const unsigned char*) fData;
+    const unsigned char* src = (const unsigned char*) srcPtr;
+    bool padTwo = false;
+    bool padThree = false;
+    const unsigned char* end = src + size;
+    while (src < end) {
+        unsigned char bytes[4];
+        int byte = 0;
+        do {
+            unsigned char srcByte = *src++;
+            if (srcByte == 0)
+                goto goHome;
+            if (srcByte <= ' ')
+                continue; // treat as white space
+            if (srcByte < '+' || srcByte > 'z')
+                return kBadCharError;
+            signed char decoded = decodeData[srcByte - '+'];
+            bytes[byte] = decoded;
+            if (decoded < 0) {
+                if (decoded == DecodePad) 
+                    goto handlePad;
+                return kBadCharError;
+            } else
+                byte++;
+            if (*src)
+                continue;
+            if (byte == 0)
+                goto goHome;
+            if (byte == 4)
+                break;
+handlePad:
+            if (byte < 2)
+                return kPadError;
+            padThree = true;
+            if (byte == 2)
+                padTwo = true;
+            break;
+        } while (byte < 4);
+        int two = 0;
+        int three = 0;
+        if (writeDestination) {
+            int one = (uint8_t) (bytes[0] << 2);
+            two = bytes[1];
+            one |= two >> 4;
+            two = (uint8_t) (two << 4);
+            three = bytes[2];
+            two |= three >> 2;
+            three = (uint8_t) (three << 6);
+            three |= bytes[3];
+            SkASSERT(one < 256 && two < 256 && three < 256);
+            *dst = (unsigned char) one;
+        }
+        dst++;
+        if (padTwo) 
+            break;
+        if (writeDestination)
+            *dst = (unsigned char) two;
+        dst++;
+        if (padThree)
+            break;
+        if (writeDestination)
+            *dst = (unsigned char) three;
+        dst++;
+    }
+goHome:
+    fLength = dst - dstStart;
+    return kNoError;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  
+#pragma warning ( pop )
+#endif
+
+size_t SkBase64::Encode(const void* srcPtr, size_t length, void* dstPtr, const char* encodeMap) {
+    const char* encode;
+    if (NULL == encodeMap) {
+        encode = default_encode;
+    } else {
+        encode = encodeMap;
+    }
+    const unsigned char* src = (const unsigned char*) srcPtr;
+    unsigned char* dst = (unsigned char*) dstPtr;
+    if (dst) {
+        size_t remainder = length % 3;
+        const unsigned char* end = &src[length - remainder];
+        while (src < end) {
+            unsigned a = *src++;
+            unsigned b = *src++;
+            unsigned c = *src++;
+            int      d = c & 0x3F;
+            c = (c >> 6 | b << 2) & 0x3F; 
+            b = (b >> 4 | a << 4) & 0x3F;
+            a = a >> 2;
+            *dst++ = encode[a];
+            *dst++ = encode[b];
+            *dst++ = encode[c];
+            *dst++ = encode[d];
+        }
+        if (remainder > 0) {
+            int k1 = 0;
+            int k2 = EncodePad;
+            int a = (uint8_t) *src++;
+            if (remainder == 2)
+            {
+                int b = *src++;
+                k1 = b >> 4;
+                k2 = (b << 2) & 0x3F;
+            }
+            *dst++ = encode[a >> 2];
+            *dst++ = encode[(k1 | a << 4) & 0x3F];
+            *dst++ = encode[k2];
+            *dst++ = encode[EncodePad];
+        }
+    }
+    return (length + 2) / 3 * 4;
+}
+
+SkBase64::Error SkBase64::decode(const char* src, size_t len) {
+    Error err = decode(src, len, false);
+    SkASSERT(err == kNoError);
+    if (err != kNoError)
+        return err;
+    fData = new char[fLength];  // should use sk_malloc/sk_free
+    decode(src, len, true);
+    return kNoError;
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkBase64::UnitTest() {
+    signed char all[256];
+    for (int index = 0; index < 256; index++)
+        all[index] = (signed char) (index + 1);
+    for (int offset = 0; offset < 6; offset++) {
+        size_t length = 256 - offset;
+        size_t encodeLength = Encode(all + offset, length, NULL);
+        char* src = (char*)sk_malloc_throw(encodeLength + 1);
+        Encode(all + offset, length, src);
+        src[encodeLength] = '\0';
+        SkBase64 tryMe;
+        tryMe.decode(src, encodeLength);
+        SkASSERT(length == tryMe.fLength);
+        SkASSERT(strcmp((const char*) (all + offset), tryMe.fData) == 0);
+        sk_free(src);
+        delete[] tryMe.fData;
+    }
+}
+#endif
+
+
diff --git a/legacy/src/utils/SkBase64.h b/legacy/src/utils/SkBase64.h
new file mode 100644
index 0000000..4f3b323
--- /dev/null
+++ b/legacy/src/utils/SkBase64.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 SkBase64_DEFINED
+#define SkBase64_DEFINED
+
+#include "SkTypes.h"
+
+struct SkBase64 {
+public:
+    enum Error {
+        kNoError,
+        kPadError,
+        kBadCharError
+    };
+
+    SkBase64();
+    Error decode(const char* src, size_t length);
+    char* getData() { return fData; }
+    /**
+       Base64 encodes src into dst. encode is a pointer to at least 65 chars.
+       encode[64] will be used as the pad character. Encodings other than the
+       default encoding cannot be decoded.
+    */
+    static size_t Encode(const void* src, size_t length, void* dest, const char* encode = NULL);
+
+#ifdef SK_SUPPORT_UNITTEST
+    static void UnitTest();
+#endif
+private:
+    Error decode(const void* srcPtr, size_t length, bool writeDestination);
+
+    size_t fLength;
+    char* fData;
+    friend class SkImage;
+};
+
+#endif // SkBase64_DEFINED
diff --git a/legacy/src/utils/SkBoundaryPatch.cpp b/legacy/src/utils/SkBoundaryPatch.cpp
new file mode 100644
index 0000000..37f59b4
--- /dev/null
+++ b/legacy/src/utils/SkBoundaryPatch.cpp
@@ -0,0 +1,80 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkBoundaryPatch.h"
+
+SkBoundaryPatch::SkBoundaryPatch() : fBoundary(NULL) {}
+
+SkBoundaryPatch::~SkBoundaryPatch() {
+    SkSafeUnref(fBoundary);
+}
+
+SkBoundary* SkBoundaryPatch::setBoundary(SkBoundary* b) {
+    SkRefCnt_SafeAssign(fBoundary, b);
+    return b;
+}
+
+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));
+}
+
+SkPoint SkBoundaryPatch::eval(SkScalar unitU, SkScalar unitV) {
+    SkBoundary* b = fBoundary;
+    SkPoint u = SkPointInterp(b->eval(SkBoundary::kLeft, SK_Scalar1 - unitV),
+                              b->eval(SkBoundary::kRight, unitV),
+                              unitU);
+    SkPoint v = SkPointInterp(b->eval(SkBoundary::kTop, unitU),
+                              b->eval(SkBoundary::kBottom, SK_Scalar1 - unitU),
+                              unitV);
+    return SkMakePoint(SkScalarAve(u.fX, v.fX),
+                       SkScalarAve(u.fY, v.fY));
+}
+
+bool SkBoundaryPatch::evalPatch(SkPoint verts[], int rows, int cols) {
+    if (rows < 2 || cols < 2) {
+        return false;
+    }
+
+    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++) {
+            *verts++ = this->eval(x * invR, yy);
+        }
+    }
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+#include "SkGeometry.h"
+
+SkPoint SkLineBoundary::eval(Edge e, SkScalar t) {
+    SkASSERT((unsigned)e < 4);
+    return SkPointInterp(fPts[e], fPts[(e + 1) & 3], t);
+}
+
+SkPoint SkCubicBoundary::eval(Edge e, SkScalar t) {
+    SkASSERT((unsigned)e < 4);
+
+    // ensure our 4th cubic wraps to the start of the first
+    fPts[12] = fPts[0];
+
+    SkPoint loc;
+    SkEvalCubicAt(&fPts[e * 3], t, &loc, NULL, NULL);
+    return loc;
+}
+
diff --git a/legacy/src/utils/SkCamera.cpp b/legacy/src/utils/SkCamera.cpp
new file mode 100644
index 0000000..ac6fa0f
--- /dev/null
+++ b/legacy/src/utils/SkCamera.cpp
@@ -0,0 +1,426 @@
+
+/*
+ * 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 "SkCamera.h"
+
+static SkScalar SkScalarDotDiv(int count, const SkScalar a[], int step_a,
+                               const SkScalar b[], int step_b,
+                               SkScalar denom) {
+#ifdef SK_SCALAR_IS_FLOAT
+    float prod = 0;
+    for (int i = 0; i < count; i++) {
+        prod += a[0] * b[0];
+        a += step_a;
+        b += step_b;
+    }
+    return prod / denom;
+#else
+    Sk64    prod, tmp;
+
+    prod.set(0);
+    for (int i = 0; i < count; i++) {
+        tmp.setMul(a[0], b[0]);
+        prod.add(tmp);
+        a += step_a;
+        b += step_b;
+    }
+    prod.div(denom, Sk64::kRound_DivOption);
+    return prod.get32();
+#endif
+}
+
+static SkScalar SkScalarDot(int count, const SkScalar a[], int step_a,
+                                       const SkScalar b[], int step_b) {
+#ifdef SK_SCALAR_IS_FLOAT
+    float prod = 0;
+    for (int i = 0; i < count; i++) {
+        prod += a[0] * b[0];
+        a += step_a;
+        b += step_b;
+    }
+    return prod;
+#else
+    Sk64    prod, tmp;
+
+    prod.set(0);
+    for (int i = 0; i < count; i++) {
+        tmp.setMul(a[0], b[0]);
+        prod.add(tmp);
+        a += step_a;
+        b += step_b;
+    }
+    return prod.getFixed();
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkUnitScalar SkPoint3D::normalize(SkUnit3D* unit) const {
+#ifdef SK_SCALAR_IS_FLOAT
+    float mag = sk_float_sqrt(fX*fX + fY*fY + fZ*fZ);
+    if (mag) {
+        float scale = 1.0f / mag;
+        unit->fX = fX * scale;
+        unit->fY = fY * scale;
+        unit->fZ = fZ * scale;
+    } else {
+        unit->fX = unit->fY = unit->fZ = 0;
+    }
+#else
+    Sk64    tmp1, tmp2;
+
+    tmp1.setMul(fX, fX);
+    tmp2.setMul(fY, fY);
+    tmp1.add(tmp2);
+    tmp2.setMul(fZ, fZ);
+    tmp1.add(tmp2);
+
+    SkFixed mag = tmp1.getSqrt();
+    if (mag) {
+        // what if mag < SK_Fixed1 ??? we will underflow the fixdiv
+        SkFixed scale = SkFixedDiv(SK_Fract1, mag);
+        unit->fX = SkFixedMul(fX, scale);
+        unit->fY = SkFixedMul(fY, scale);
+        unit->fZ = SkFixedMul(fZ, scale);
+    } else {
+        unit->fX = unit->fY = unit->fZ = 0;
+    }
+#endif
+    return mag;
+}
+
+SkUnitScalar SkUnit3D::Dot(const SkUnit3D& a, const SkUnit3D& b) {
+    return  SkUnitScalarMul(a.fX, b.fX) +
+            SkUnitScalarMul(a.fY, b.fY) +
+            SkUnitScalarMul(a.fZ, b.fZ);
+}
+
+void SkUnit3D::Cross(const SkUnit3D& a, const SkUnit3D& b, SkUnit3D* cross) {
+    SkASSERT(cross);
+
+    // use x,y,z, in case &a == cross or &b == cross
+
+    SkScalar x = SkUnitScalarMul(a.fY, b.fZ) - SkUnitScalarMul(a.fZ, b.fY);
+    SkScalar y = SkUnitScalarMul(a.fZ, b.fX) - SkUnitScalarMul(a.fX, b.fY);
+    SkScalar z = SkUnitScalarMul(a.fX, b.fY) - SkUnitScalarMul(a.fY, b.fX);
+
+    cross->set(x, y, z);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPatch3D::SkPatch3D() {
+    this->reset();
+}
+
+void SkPatch3D::reset() {
+    fOrigin.set(0, 0, 0);
+    fU.set(SK_Scalar1, 0, 0);
+    fV.set(0, -SK_Scalar1, 0);
+}
+
+void SkPatch3D::transform(const SkMatrix3D& m, SkPatch3D* dst) const {
+    if (dst == NULL) {
+        dst = (SkPatch3D*)this;
+    }
+    m.mapVector(fU, &dst->fU);
+    m.mapVector(fV, &dst->fV);
+    m.mapPoint(fOrigin, &dst->fOrigin);
+}
+
+SkScalar SkPatch3D::dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const {
+    SkScalar cx = SkScalarMul(fU.fY, fV.fZ) - SkScalarMul(fU.fZ, fV.fY);
+    SkScalar cy = SkScalarMul(fU.fZ, fV.fX) - SkScalarMul(fU.fX, fV.fY);
+    SkScalar cz = SkScalarMul(fU.fX, fV.fY) - SkScalarMul(fU.fY, fV.fX);
+
+    return SkScalarMul(cx, dx) + SkScalarMul(cy, dy) + SkScalarMul(cz, dz);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix3D::reset() {
+    memset(fMat, 0, sizeof(fMat));
+    fMat[0][0] = fMat[1][1] = fMat[2][2] = SK_Scalar1;
+}
+
+void SkMatrix3D::setTranslate(SkScalar x, SkScalar y, SkScalar z) {
+    memset(fMat, 0, sizeof(fMat));
+    fMat[0][0] = x;
+    fMat[1][1] = y;
+    fMat[2][2] = z;
+}
+
+void SkMatrix3D::setRotateX(SkScalar degX) {
+    SkScalar    s, c;
+
+    s = SkScalarSinCos(SkDegreesToRadians(degX), &c);
+    this->setRow(0, SK_Scalar1, 0, 0);
+    this->setRow(1, 0, c, -s);
+    this->setRow(2, 0, s, c);
+}
+
+void SkMatrix3D::setRotateY(SkScalar degY) {
+    SkScalar    s, c;
+
+    s = SkScalarSinCos(SkDegreesToRadians(degY), &c);
+    this->setRow(0, c, 0, -s);
+    this->setRow(1, 0, SK_Scalar1, 0);
+    this->setRow(2, s, 0, c);
+}
+
+void SkMatrix3D::setRotateZ(SkScalar degZ) {
+    SkScalar    s, c;
+
+    s = SkScalarSinCos(SkDegreesToRadians(degZ), &c);
+    this->setRow(0, c, -s, 0);
+    this->setRow(1, s, c, 0);
+    this->setRow(2, 0, 0, SK_Scalar1);
+}
+
+void SkMatrix3D::preTranslate(SkScalar x, SkScalar y, SkScalar z) {
+    SkScalar col[3] = { x, y, z};
+
+    for (int i = 0; i < 3; i++) {
+        fMat[i][3] += SkScalarDot(3, &fMat[i][0], 1, col, 1);
+    }
+}
+
+void SkMatrix3D::preRotateX(SkScalar degX) {
+    SkMatrix3D m;
+    m.setRotateX(degX);
+    this->setConcat(*this, m);
+}
+
+void SkMatrix3D::preRotateY(SkScalar degY) {
+    SkMatrix3D m;
+    m.setRotateY(degY);
+    this->setConcat(*this, m);
+}
+
+void SkMatrix3D::preRotateZ(SkScalar degZ) {
+    SkMatrix3D m;
+    m.setRotateZ(degZ);
+    this->setConcat(*this, m);
+}
+
+void SkMatrix3D::setConcat(const SkMatrix3D& a, const SkMatrix3D& b) {
+    SkMatrix3D  tmp;
+    SkMatrix3D* c = this;
+
+    if (this == &a || this == &b) {
+        c = &tmp;
+    }
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            c->fMat[i][j] = SkScalarDot(3, &a.fMat[i][0], 1, &b.fMat[0][j], 4);
+        }
+        c->fMat[i][3] = SkScalarDot(3, &a.fMat[i][0], 1,
+                                    &b.fMat[0][3], 4) + a.fMat[i][3];
+    }
+
+    if (c == &tmp) {
+        *this = tmp;
+    }
+}
+
+void SkMatrix3D::mapPoint(const SkPoint3D& src, SkPoint3D* dst) const {
+    SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1) + fMat[0][3];
+    SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1) + fMat[1][3];
+    SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1) + fMat[2][3];
+    dst->set(x, y, z);
+}
+
+void SkMatrix3D::mapVector(const SkVector3D& src, SkVector3D* dst) const {
+    SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1);
+    SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1);
+    SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1);
+    dst->set(x, y, z);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCamera3D::SkCamera3D() {
+    this->reset();
+}
+
+void SkCamera3D::reset() {
+    fLocation.set(0, 0, -SkIntToScalar(576));   // 8 inches backward
+    fAxis.set(0, 0, SK_Scalar1);                // forward
+    fZenith.set(0, -SK_Scalar1, 0);             // up
+
+    fObserver.set(0, 0, fLocation.fZ);
+
+    fNeedToUpdate = true;
+}
+
+void SkCamera3D::update() {
+    fNeedToUpdate = true;
+}
+
+void SkCamera3D::doUpdate() const {
+    SkUnit3D    axis, zenith, cross;
+
+    fAxis.normalize(&axis);
+
+    {
+        SkScalar dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&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);
+    }
+
+    SkUnit3D::Cross(axis, zenith, &cross);
+
+    {
+        SkMatrix* orien = &fOrientation;
+        SkScalar x = fObserver.fX;
+        SkScalar y = fObserver.fY;
+        SkScalar z = fObserver.fZ;
+
+        orien->set(SkMatrix::kMScaleX, SkUnitScalarMul(x, axis.fX) - SkUnitScalarMul(z, cross.fX));
+        orien->set(SkMatrix::kMSkewX,  SkUnitScalarMul(x, axis.fY) - SkUnitScalarMul(z, cross.fY));
+        orien->set(SkMatrix::kMTransX, SkUnitScalarMul(x, axis.fZ) - SkUnitScalarMul(z, cross.fZ));
+        orien->set(SkMatrix::kMSkewY,  SkUnitScalarMul(y, axis.fX) - SkUnitScalarMul(z, zenith.fX));
+        orien->set(SkMatrix::kMScaleY, SkUnitScalarMul(y, axis.fY) - SkUnitScalarMul(z, zenith.fY));
+        orien->set(SkMatrix::kMTransY, SkUnitScalarMul(y, axis.fZ) - SkUnitScalarMul(z, zenith.fZ));
+        orien->set(SkMatrix::kMPersp0, axis.fX);
+        orien->set(SkMatrix::kMPersp1, axis.fY);
+        orien->set(SkMatrix::kMPersp2, axis.fZ);
+    }
+}
+
+void SkCamera3D::patchToMatrix(const SkPatch3D& quilt, SkMatrix* matrix) const {
+    if (fNeedToUpdate) {
+        this->doUpdate();
+        fNeedToUpdate = false;
+    }
+
+    const SkScalar* mapPtr = (const SkScalar*)(const void*)&fOrientation;
+    const SkScalar* patchPtr;
+    SkPoint3D       diff;
+    SkScalar        dot;
+
+    diff.fX = quilt.fOrigin.fX - fLocation.fX;
+    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));
+
+    patchPtr = (const SkScalar*)&quilt;
+    matrix->set(SkMatrix::kMScaleX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
+    matrix->set(SkMatrix::kMSkewY,  SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
+    matrix->set(SkMatrix::kMPersp0, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));
+
+    patchPtr += 3;
+    matrix->set(SkMatrix::kMSkewX,  SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
+    matrix->set(SkMatrix::kMScaleY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
+    matrix->set(SkMatrix::kMPersp1, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));
+
+    patchPtr = (const SkScalar*)(const void*)&diff;
+    matrix->set(SkMatrix::kMTransX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
+    matrix->set(SkMatrix::kMTransY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
+    matrix->set(SkMatrix::kMPersp2, SK_UnitScalar1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Sk3DView::Sk3DView() {
+    fInitialRec.fMatrix.reset();
+    fRec = &fInitialRec;
+}
+
+Sk3DView::~Sk3DView() {
+    Rec* rec = fRec;
+    while (rec != &fInitialRec) {
+        Rec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+}
+
+void Sk3DView::save() {
+    Rec* rec = SkNEW(Rec);
+    rec->fNext = fRec;
+    rec->fMatrix = fRec->fMatrix;
+    fRec = rec;
+}
+
+void Sk3DView::restore() {
+    SkASSERT(fRec != &fInitialRec);
+    Rec* next = fRec->fNext;
+    SkDELETE(fRec);
+    fRec = next;
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+void Sk3DView::setCameraLocation(SkScalar x, SkScalar y, SkScalar z) {
+    // the camera location is passed in inches, set in pt
+    SkScalar lz = z * SkFloatToScalar(72.0f);
+    fCamera.fLocation.set(x * SkFloatToScalar(72.0f), y * SkFloatToScalar(72.0f), lz);
+    fCamera.fObserver.set(0, 0, lz);
+    fCamera.update();
+    
+}
+
+SkScalar Sk3DView::getCameraLocationX() {
+    return fCamera.fLocation.fX / SkFloatToScalar(72.0f);
+}
+
+SkScalar Sk3DView::getCameraLocationY() {
+    return fCamera.fLocation.fY / SkFloatToScalar(72.0f);
+}
+
+SkScalar Sk3DView::getCameraLocationZ() {
+    return fCamera.fLocation.fZ / SkFloatToScalar(72.0f);
+}
+#endif
+
+void Sk3DView::translate(SkScalar x, SkScalar y, SkScalar z) {
+    fRec->fMatrix.preTranslate(x, y, z);
+}
+
+void Sk3DView::rotateX(SkScalar deg) {
+    fRec->fMatrix.preRotateX(deg);
+}
+
+void Sk3DView::rotateY(SkScalar deg) {
+    fRec->fMatrix.preRotateY(deg);
+}
+
+void Sk3DView::rotateZ(SkScalar deg) {
+    fRec->fMatrix.preRotateZ(deg);
+}
+
+SkScalar Sk3DView::dotWithNormal(SkScalar x, SkScalar y, SkScalar z) const {
+    SkPatch3D   patch;
+    patch.transform(fRec->fMatrix);
+    return patch.dotWith(x, y, z);
+}
+
+void Sk3DView::getMatrix(SkMatrix* matrix) const {
+    if (matrix != NULL) {
+        SkPatch3D   patch;
+        patch.transform(fRec->fMatrix);
+        fCamera.patchToMatrix(patch, matrix);
+    }
+}
+
+#include "SkCanvas.h"
+
+void Sk3DView::applyToCanvas(SkCanvas* canvas) const {
+    SkMatrix    matrix;
+    
+    this->getMatrix(&matrix);
+    canvas->concat(matrix);
+}
+
diff --git a/src/utils/SkColorMatrix.cpp b/legacy/src/utils/SkColorMatrix.cpp
similarity index 100%
rename from src/utils/SkColorMatrix.cpp
rename to legacy/src/utils/SkColorMatrix.cpp
diff --git a/legacy/src/utils/SkCubicInterval.cpp b/legacy/src/utils/SkCubicInterval.cpp
new file mode 100644
index 0000000..6c6b0a9
--- /dev/null
+++ b/legacy/src/utils/SkCubicInterval.cpp
@@ -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.
+ */
+#include "SkCubicInterval.h"
+
+static SkScalar eval_cubic(SkScalar c1, SkScalar c2, SkScalar c3,
+                           SkScalar t) {
+    return SkScalarMul(SkScalarMul(SkScalarMul(c3, t) + c2, t) + c1, t);
+}
+
+static SkScalar find_cubic_t(SkScalar c1, SkScalar c2, SkScalar c3,
+                             SkScalar targetX) {
+    SkScalar minT = 0;
+    SkScalar maxT = SK_Scalar1;
+    SkScalar t;
+
+    for (;;) {
+        t = SkScalarAve(minT, maxT);
+        SkScalar x = eval_cubic(c1, c2, c3, t);
+        if (SkScalarNearlyZero(x - targetX)) {
+            break;
+        }
+        // subdivide the range and try again
+        if (x < targetX) {
+            minT = t;
+        } else {
+            maxT = t;
+        }
+    }
+    return t;
+}
+
+/*
+    a(1-t)^3 + 3bt(1-t)^2 + 3ct^2(1-t) + dt^3
+    a: [0, 0]
+    d: [1, 1]
+
+    3bt - 6bt^2 + 3bt^3 + 3ct^2 - 3ct^3 + t^3
+    C1 = t^1: 3b
+    C2 = t^2: 3c - 6b
+    C3 = t^3: 3b - 3c + 1
+
+    ((C3*t + C2)*t + C1)*t
+ */
+SkScalar SkEvalCubicInterval(SkScalar x1, SkScalar y1,
+                             SkScalar x2, SkScalar y2,
+                             SkScalar unitX) {
+    x1 = SkScalarPin(x1, 0, SK_Scalar1);
+    x2 = SkScalarPin(x2, 0, SK_Scalar1);
+    unitX = SkScalarPin(unitX, 0, SK_Scalar1);
+
+    // First compute our coefficients in X
+    x1 *= 3;
+    x2 *= 3;
+
+    // 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/legacy/src/utils/SkCullPoints.cpp b/legacy/src/utils/SkCullPoints.cpp
new file mode 100644
index 0000000..a903075
--- /dev/null
+++ b/legacy/src/utils/SkCullPoints.cpp
@@ -0,0 +1,148 @@
+
+/*
+ * 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 "SkCullPoints.h"
+#include "Sk64.h"
+
+static bool cross_product_is_neg(const SkIPoint& v, int dx, int dy) {
+#if 0
+    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);
+    return tmp0.isNeg() != 0;
+#endif
+}
+
+bool SkCullPoints::sect_test(int x0, int y0, int x1, int y1) const {
+    const SkIRect& r = fR;
+
+    if ((x0 < r.fLeft    && x1 < r.fLeft) ||
+        (x0 > r.fRight   && x1 > r.fRight) ||
+        (y0 < r.fTop     && y1 < r.fTop) ||
+        (y0 > r.fBottom  && y1 > r.fBottom)) {
+        return false;
+    }
+
+    // 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;
+    }
+
+    // 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++) {
+        if (cross_product_is_neg(vec, x0 - rAsQuad[i].fX, y0 - rAsQuad[i].fY) != isNeg) {
+            return true;
+        }
+    }
+    return false;   // we didn't intersect
+}
+
+static void toQuad(const SkIRect& r, SkIPoint quad[4]) {
+    SkASSERT(quad);
+
+    quad[0].set(r.fLeft, r.fTop);
+    quad[1].set(r.fRight, r.fTop);
+    quad[2].set(r.fRight, r.fBottom);
+    quad[3].set(r.fLeft, r.fBottom);
+}
+
+SkCullPoints::SkCullPoints() {
+    SkIRect    r;
+    r.setEmpty();
+    this->reset(r);
+}
+
+SkCullPoints::SkCullPoints(const SkIRect& r) {
+    this->reset(r);
+}
+
+void SkCullPoints::reset(const SkIRect& r) {
+    fR = r;
+    toQuad(fR, fAsQuad);
+    fPrevPt.set(0, 0);
+    fPrevResult = kNo_Result;
+}
+
+void SkCullPoints::moveTo(int x, int y) {
+    fPrevPt.set(x, y);
+    fPrevResult = kNo_Result;   // so we trigger a movetolineto later
+}
+
+SkCullPoints::LineToResult SkCullPoints::lineTo(int x, int y, SkIPoint line[]) {
+    SkASSERT(line != NULL);
+
+    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 {
+            result = kMoveToLineTo_Result;
+        }
+    }
+
+    fPrevPt.set(x, y);
+    fPrevResult = result;
+
+    return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPath.h"
+
+SkCullPointsPath::SkCullPointsPath()
+    : fCP(), fPath(NULL) {
+}
+
+SkCullPointsPath::SkCullPointsPath(const SkIRect& r, SkPath* dst)
+    : fCP(r), fPath(dst) {
+}
+
+void SkCullPointsPath::reset(const SkIRect& r, SkPath* dst) {
+    fCP.reset(r);
+    fPath = dst;
+}
+
+void SkCullPointsPath::moveTo(int x, int y) {
+    fCP.moveTo(x, y);
+}
+
+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));
+        // fall through to the lineto case
+    case SkCullPoints::kLineTo_Result:
+        fPath->lineTo(SkIntToScalar(pts[1].fX), SkIntToScalar(pts[1].fY));
+        break;
+    default:
+        break;
+    }
+}
+
diff --git a/legacy/src/utils/SkDebugTrace.h b/legacy/src/utils/SkDebugTrace.h
new file mode 100644
index 0000000..447418e
--- /dev/null
+++ b/legacy/src/utils/SkDebugTrace.h
@@ -0,0 +1,26 @@
+
+/*
+ * 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 SkUserTrace_DEFINED
+#define SkUserTrace_DEFINED
+
+/* Sample implementation of SkUserTrace that routes all of the
+   trace macros to debug output stream.
+   To use this, redefine SK_USER_TRACE_INCLUDE_FILE in
+   include/config/SkUserConfig.h to point to this file
+*/
+#define SK_TRACE_EVENT0(event) \
+  SkDebugf("Trace: %s\n", event)
+#define SK_TRACE_EVENT1(event, name1, value1) \
+  SkDebugf("Trace: %s (%s=%s)\n", event, name1, value1)
+#define SK_TRACE_EVENT2(event, name1, value1, name2, value2) \
+  SkDebugf("Trace: %s (%s=%s, %s=%s)\n", event, name1, value1, name2, value2)
+
+#endif
+
+
diff --git a/legacy/src/utils/SkDeferredCanvas.cpp b/legacy/src/utils/SkDeferredCanvas.cpp
new file mode 100644
index 0000000..e965050
--- /dev/null
+++ b/legacy/src/utils/SkDeferredCanvas.cpp
@@ -0,0 +1,595 @@
+
+/*
+ * 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 "SkDeferredCanvas.h"
+
+#include "SkPaint.h"
+#include "SkShader.h"
+#include "SkColorFilter.h"
+#include "SkDrawFilter.h"
+
+namespace {
+
+bool isPaintOpaque(const SkPaint* paint, 
+                   const SkBitmap* bmpReplacesShader = NULL) {
+    // TODO: SkXfermode should have a virtual isOpaque method, which would
+    // make it possible to test modes that do not have a Coeff representation.
+
+    if (!paint) {
+        return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
+    }
+
+    SkXfermode::Coeff srcCoeff, dstCoeff;
+    if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
+        switch (dstCoeff) {
+        case SkXfermode::kZero_Coeff:
+            return true;
+        case SkXfermode::kISA_Coeff:
+            if (paint->getAlpha() != 255) {
+                break;
+            }
+            if (bmpReplacesShader) {
+                if (!bmpReplacesShader->isOpaque()) {
+                    break;
+                }
+            } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
+                break;
+            }
+            if (paint->getColorFilter() && 
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSA_Coeff:
+            if (paint->getAlpha() != 0) {
+                break;
+            }
+            if (paint->getColorFilter() && 
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSC_Coeff:
+            if (paint->getColor() != 0) { // all components must be 0
+                break;
+            }
+            if (bmpReplacesShader || paint->getShader()) {
+                break;
+            }
+            if (paint->getColorFilter() && (
+                (paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        default:
+            break;
+        }
+    }
+    return false;
+}
+
+} // unnamed namespace
+
+SkDeferredCanvas::SkDeferredCanvas() {
+    init();
+}
+
+SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
+    init();
+    setDevice(device);
+}
+
+SkDeferredCanvas::SkDeferredCanvas(SkDevice* device, 
+                                   DeviceContext* deviceContext) {
+    init();
+    setDevice(device);
+    setDeviceContext(deviceContext);
+}
+
+void SkDeferredCanvas::init() {
+    fDeferredDrawing = true; // On by default
+}
+
+void SkDeferredCanvas::validate() const {
+    SkASSERT(getDevice());
+}
+
+SkCanvas* SkDeferredCanvas::drawingCanvas() const {
+    validate();
+    return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
+        getDeferredDevice()->immediateCanvas();
+}
+
+void SkDeferredCanvas::flushIfNeeded(const SkBitmap& bitmap) {
+    validate();
+    if (fDeferredDrawing) {
+        getDeferredDevice()->flushIfNeeded(bitmap);
+    }
+}
+
+SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
+    return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
+}
+
+void SkDeferredCanvas::setDeferredDrawing(bool val) {
+    validate(); // Must set device before calling this method
+    SkASSERT(drawingCanvas()->getSaveCount() == 1);
+    if (val != fDeferredDrawing) {
+        if (fDeferredDrawing) {
+            // Going live.
+            getDeferredDevice()->flushPending();
+        }
+        fDeferredDrawing = val;
+    }
+}
+
+SkDeferredCanvas::~SkDeferredCanvas() {
+}
+
+SkDevice* SkDeferredCanvas::setDevice(SkDevice* device) {
+    INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
+    return device;
+}
+
+SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
+    DeviceContext* deviceContext) {
+
+    DeferredDevice* deferredDevice = getDeferredDevice();
+    SkASSERT(deferredDevice);
+    if (deferredDevice) {
+        deferredDevice->setDeviceContext(deviceContext);
+    }
+    return deviceContext;
+}
+
+bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
+                                   const SkPaint* paint) const {
+    SkCanvas* canvas = drawingCanvas();
+    SkISize canvasSize = getDeviceSize();
+    if (rect) {
+        if (!canvas->getTotalMatrix().rectStaysRect()) {
+            return false; // conservative
+        }
+
+        SkRect transformedRect;
+        canvas->getTotalMatrix().mapRect(&transformedRect, *rect);
+
+        if (paint) {
+            SkPaint::Style paintStyle = paint->getStyle();
+            if (!(paintStyle == SkPaint::kFill_Style || 
+                paintStyle == SkPaint::kStrokeAndFill_Style)) {
+                return false;
+            }
+            if (paint->getMaskFilter() || paint->getLooper()
+                || paint->getPathEffect() || paint->getImageFilter()) {
+                return false; // conservative
+            }
+        }
+
+        // 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) || 
+            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;
+}
+
+int SkDeferredCanvas::save(SaveFlags flags) {
+    drawingCanvas()->save(flags);
+    return this->INHERITED::save(flags);
+}
+
+int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                                SaveFlags flags) {
+    drawingCanvas()->saveLayer(bounds, paint, flags);
+    int count = this->INHERITED::save(flags);
+    this->clipRectBounds(bounds, flags, NULL);
+    return count;
+}
+
+void SkDeferredCanvas::restore() {
+    drawingCanvas()->restore();
+    this->INHERITED::restore();
+}
+
+bool SkDeferredCanvas::isDrawingToLayer() const {
+    return drawingCanvas()->isDrawingToLayer();
+}
+
+bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy) {
+    drawingCanvas()->translate(dx, dy);
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy) {
+    drawingCanvas()->scale(sx, sy);
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkDeferredCanvas::rotate(SkScalar degrees) {
+    drawingCanvas()->rotate(degrees);
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy) {
+    drawingCanvas()->skew(sx, sy);
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkDeferredCanvas::concat(const SkMatrix& matrix) {
+    drawingCanvas()->concat(matrix);
+    return this->INHERITED::concat(matrix);
+}
+
+void SkDeferredCanvas::setMatrix(const SkMatrix& matrix) {
+    drawingCanvas()->setMatrix(matrix);
+    this->INHERITED::setMatrix(matrix);
+}
+
+bool SkDeferredCanvas::clipRect(const SkRect& rect,
+                                SkRegion::Op op,
+                                bool doAntiAlias) {
+    drawingCanvas()->clipRect(rect, op, doAntiAlias);
+    return this->INHERITED::clipRect(rect, op, doAntiAlias);
+}
+
+bool SkDeferredCanvas::clipPath(const SkPath& path,
+                                SkRegion::Op op,
+                                bool doAntiAlias) {
+    drawingCanvas()->clipPath(path, op, doAntiAlias);
+    return this->INHERITED::clipPath(path, op, doAntiAlias);
+}
+
+bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
+                                  SkRegion::Op op) {
+    drawingCanvas()->clipRegion(deviceRgn, op);
+    return this->INHERITED::clipRegion(deviceRgn, op);
+}
+
+void SkDeferredCanvas::clear(SkColor color) {
+    // purge pending commands
+    if (fDeferredDrawing) {
+        getDeferredDevice()->contentsCleared();
+    }
+
+    drawingCanvas()->clear(color);
+}
+
+void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
+    if (fDeferredDrawing && isFullFrame(NULL, &paint) && 
+        isPaintOpaque(&paint)) {
+        getDeferredDevice()->contentsCleared();
+    }
+
+    drawingCanvas()->drawPaint(paint);
+}
+
+void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
+                                  const SkPoint pts[], const SkPaint& paint) {
+    drawingCanvas()->drawPoints(mode, count, pts, paint);
+}
+
+void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    if (fDeferredDrawing && isFullFrame(&rect, &paint) && 
+        isPaintOpaque(&paint)) {
+        getDeferredDevice()->contentsCleared();
+    }
+
+    drawingCanvas()->drawRect(rect, paint);
+}
+
+void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    drawingCanvas()->drawPath(path, paint);
+}
+
+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) &&
+        isPaintOpaque(paint, &bitmap)) {
+        getDeferredDevice()->contentsCleared();
+    }
+
+    drawingCanvas()->drawBitmap(bitmap, left, top, paint);
+    flushIfNeeded(bitmap);
+}
+
+void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap, 
+                                      const SkIRect* src,
+                                      const SkRect& dst,
+                                      const SkPaint* paint) {
+    if (fDeferredDrawing && 
+        isFullFrame(&dst, paint) &&
+        isPaintOpaque(paint, &bitmap)) {
+        getDeferredDevice()->contentsCleared();
+    }
+
+    drawingCanvas()->drawBitmapRect(bitmap, src,
+                                    dst, paint);
+    flushIfNeeded(bitmap);
+}
+
+
+void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
+                                        const SkMatrix& m,
+                                        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);
+}
+
+void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
+                                      const SkIRect& center, const SkRect& dst,
+                                      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);
+}
+
+void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
+                                  const SkPaint* paint) {
+    SkRect bitmapRect = SkRect::MakeXYWH(
+        SkIntToScalar(left),
+        SkIntToScalar(top), 
+        SkIntToScalar(bitmap.width()),
+        SkIntToScalar(bitmap.height()));
+    if (fDeferredDrawing && 
+        isFullFrame(&bitmapRect, paint) &&
+        isPaintOpaque(paint, &bitmap)) {
+        getDeferredDevice()->contentsCleared();
+    }
+
+    drawingCanvas()->drawSprite(bitmap, left, top,
+                                paint);
+    flushIfNeeded(bitmap);
+}
+
+void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
+                                SkScalar x, SkScalar y, const SkPaint& paint) {
+    drawingCanvas()->drawText(text, byteLength, x, y, paint);
+}
+
+void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
+                                   const SkPoint pos[], const SkPaint& paint) {
+    drawingCanvas()->drawPosText(text, byteLength, pos, paint);
+}
+
+void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                    const SkScalar xpos[], SkScalar constY,
+                                    const SkPaint& paint) {
+    drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint);
+}
+
+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);
+}
+
+void SkDeferredCanvas::drawPicture(SkPicture& picture) {
+    drawingCanvas()->drawPicture(picture);
+}
+
+void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                                    const SkPoint vertices[],
+                                    const SkPoint texs[],
+                                    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);
+}
+
+SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) {
+    drawingCanvas()->setBounder(bounder);
+    return INHERITED::setBounder(bounder);
+}
+
+SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
+    drawingCanvas()->setDrawFilter(filter); 
+    return INHERITED::setDrawFilter(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);
+}
diff --git a/legacy/src/utils/SkDumpCanvas.cpp b/legacy/src/utils/SkDumpCanvas.cpp
new file mode 100644
index 0000000..c9b4a95
--- /dev/null
+++ b/legacy/src/utils/SkDumpCanvas.cpp
@@ -0,0 +1,455 @@
+
+/*
+ * 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 "SkDumpCanvas.h"
+#include "SkPicture.h"
+#include "SkPixelRef.h"
+#include "SkString.h"
+#include <stdarg.h>
+
+// needed just to know that these are all subclassed from SkFlattenable
+#include "SkShader.h"
+#include "SkPathEffect.h"
+#include "SkXfermode.h"
+#include "SkColorFilter.h"
+#include "SkPathEffect.h"
+#include "SkMaskFilter.h"
+
+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()));
+}
+
+static void toString(const SkIRect& r, SkString* str) {
+    str->printf("[%d,%d %d:%d]", r.fLeft, r.fTop, r.width(), r.height());
+}
+
+static void dumpVerbs(const SkPath& path, SkString* str) {
+    SkPath::Iter iter(path, false);
+    SkPoint pts[4];
+    for (;;) {
+        switch (iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                str->appendf(" M%g,%g", pts[0].fX, pts[0].fY);
+                break;
+            case SkPath::kLine_Verb:
+                str->appendf(" L%g,%g", pts[0].fX, pts[0].fY);
+                break;
+            case SkPath::kQuad_Verb:
+                str->appendf(" Q%g,%g,%g,%g", pts[1].fX, pts[1].fY,
+                             pts[2].fX, pts[2].fY);
+                break;
+            case SkPath::kCubic_Verb:
+                str->appendf(" C%g,%g,%g,%g,%g,%g", pts[1].fX, pts[1].fY,
+                             pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
+                break;
+            case SkPath::kClose_Verb:
+                str->append("X");
+                break;
+            case SkPath::kDone_Verb:
+                return;
+        }
+    }
+}
+
+static void toString(const SkPath& path, SkString* str) {
+    if (path.isEmpty()) {
+        str->set("path:empty");
+    } else {
+        toString(path.getBounds(), str);
+#if 1
+        SkString s;
+        dumpVerbs(path, &s);
+        str->append(s.c_str());
+#endif
+        str->append("]");
+        str->prepend("path:[");
+    }
+}
+
+static const char* toString(SkRegion::Op op) {
+    static const char* gOpNames[] = {
+        "DIFF", "SECT", "UNION", "XOR", "RDIFF", "REPLACE"
+    };
+    return gOpNames[op];
+}
+
+static void toString(const SkRegion& rgn, SkString* str) {
+    toString(rgn.getBounds(), str);
+    str->prepend("Region:[");
+    str->append("]");
+    if (rgn.isComplex()) {
+        str->append(".complex");
+    }
+}
+
+static const char* toString(SkCanvas::VertexMode vm) {
+    static const char* gVMNames[] = {
+        "TRIANGLES", "STRIP", "FAN"
+    };
+    return gVMNames[vm];
+}
+
+static const char* toString(SkCanvas::PointMode pm) {
+    static const char* gPMNames[] = {
+        "POINTS", "LINES", "POLYGON"
+    };
+    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,
+                     SkString* str) {
+    switch (enc) {
+        case SkPaint::kUTF8_TextEncoding:
+            str->printf("\"%.*s\"%s", SkMax32(len, 32), text,
+                        len > 32 ? "..." : "");
+            break;
+        case SkPaint::kUTF16_TextEncoding:
+            str->printf("\"%.*S\"%s", SkMax32(len, 32), text,
+                        len > 64 ? "..." : "");
+            break;
+        case SkPaint::kGlyphID_TextEncoding:
+            str->set("<glyphs>");
+            break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDumpCanvas::SkDumpCanvas(Dumper* dumper) : 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() {
+    SkSafeUnref(fDumper);
+}
+
+void SkDumpCanvas::dump(Verb verb, const SkPaint* paint,
+                        const char format[], ...) {
+    static const size_t BUFFER_SIZE = 1024;
+
+    char    buffer[BUFFER_SIZE];
+    va_list args;
+    va_start(args, format);
+    vsnprintf(buffer, BUFFER_SIZE, format, args);
+    va_end(args);
+
+    if (fDumper) {
+        fDumper->dump(this, verb, buffer, paint);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkDumpCanvas::save(SaveFlags flags) {
+    this->dump(kSave_Verb, NULL, "save(0x%X)", flags);
+    return this->INHERITED::save(flags);
+}
+
+int SkDumpCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                             SaveFlags flags) {
+    this->dump(kSave_Verb, paint, "saveLayer(0x%X)", flags);
+    return this->INHERITED::saveLayer(bounds, paint, flags);
+}
+
+void SkDumpCanvas::restore() {
+    this->INHERITED::restore();
+    this->dump(kRestore_Verb, NULL, "restore");
+}
+
+bool SkDumpCanvas::translate(SkScalar dx, SkScalar dy) {
+    this->dump(kMatrix_Verb, NULL, "translate(%g %g)",
+               SkScalarToFloat(dx), SkScalarToFloat(dy));
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkDumpCanvas::scale(SkScalar sx, SkScalar sy) {
+    this->dump(kMatrix_Verb, NULL, "scale(%g %g)",
+               SkScalarToFloat(sx), SkScalarToFloat(sy));
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkDumpCanvas::rotate(SkScalar degrees) {
+    this->dump(kMatrix_Verb, NULL, "rotate(%g)", SkScalarToFloat(degrees));
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkDumpCanvas::skew(SkScalar sx, SkScalar sy) {
+    this->dump(kMatrix_Verb, NULL, "skew(%g %g)",
+               SkScalarToFloat(sx), SkScalarToFloat(sy));
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkDumpCanvas::concat(const SkMatrix& matrix) {
+    SkString str;
+    matrix.toDumpString(&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);
+    this->dump(kMatrix_Verb, NULL, "setMatrix(%s)", str.c_str());
+    this->INHERITED::setMatrix(matrix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char* bool_to_aastring(bool doAA) {
+    return doAA ? "AA" : "BW";
+}
+
+bool SkDumpCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    SkString str;
+    toString(rect, &str);
+    this->dump(kClip_Verb, NULL, "clipRect(%s %s %s)", str.c_str(), toString(op),
+               bool_to_aastring(doAA));
+    return this->INHERITED::clipRect(rect, op, doAA);
+}
+
+bool SkDumpCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    SkString str;
+    toString(path, &str);
+    this->dump(kClip_Verb, NULL, "clipPath(%s %s %s)", str.c_str(), toString(op),
+               bool_to_aastring(doAA));
+    return this->INHERITED::clipPath(path, op, doAA);
+}
+
+bool SkDumpCanvas::clipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+    SkString str;
+    toString(deviceRgn, &str);
+    this->dump(kClip_Verb, NULL, "clipRegion(%s %s)", str.c_str(),
+               toString(op));
+    return this->INHERITED::clipRegion(deviceRgn, op);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDumpCanvas::drawPaint(const SkPaint& paint) {
+    this->dump(kDrawPaint_Verb, &paint, "drawPaint()");
+}
+
+void SkDumpCanvas::drawPoints(PointMode mode, size_t count,
+                               const SkPoint pts[], const SkPaint& paint) {
+    this->dump(kDrawPoints_Verb, &paint, "drawPoints(%s, %d)", toString(mode),
+               count);
+}
+
+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::drawPath(const SkPath& path, const SkPaint& paint) {
+    SkString str;
+    toString(path, &str);
+    this->dump(kDrawPath_Verb, &paint, "drawPath(%s)", str.c_str());
+}
+
+void SkDumpCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                               const SkPaint* paint) {
+    SkString str;
+    toString(bitmap, &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) {
+    SkString bs, rs;
+    toString(bitmap, &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())) {
+        SkString ss;
+        toString(*src, &ss);
+        rs.prependf("%s ", ss.c_str());
+    }
+
+    this->dump(kDrawBitmap_Verb, paint, "drawBitmapRect(%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);
+    this->dump(kDrawBitmap_Verb, paint, "drawBitmapMatrix(%s %s)",
+               bs.c_str(), ms.c_str());
+}
+
+void SkDumpCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                               const SkPaint* paint) {
+    SkString str;
+    toString(bitmap, &str);
+    this->dump(kDrawBitmap_Verb, paint, "drawSprite(%s %d %d)", str.c_str(),
+               x, y);
+}
+
+void SkDumpCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+                             SkScalar y, const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawText(%s [%d] %g %g)", str.c_str(),
+               byteLength, SkScalarToFloat(x), SkScalarToFloat(y));
+}
+
+void SkDumpCanvas::drawPosText(const void* text, size_t byteLength,
+                                const SkPoint pos[], const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawPosText(%s [%d] %g %g ...)",
+               str.c_str(), byteLength, SkScalarToFloat(pos[0].fX),
+               SkScalarToFloat(pos[0].fY));
+}
+
+void SkDumpCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                 const SkScalar xpos[], SkScalar constY,
+                                 const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawPosTextH(%s [%d] %g %g ...)",
+               str.c_str(), byteLength, SkScalarToFloat(xpos[0]),
+               SkScalarToFloat(constY));
+}
+
+void SkDumpCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                                   const SkPath& path, const SkMatrix* matrix,
+                                   const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawTextOnPath(%s [%d])",
+               str.c_str(), byteLength);
+}
+
+void SkDumpCanvas::drawPicture(SkPicture& picture) {
+    this->dump(kDrawPicture_Verb, NULL, "drawPicture(%p) %d:%d", &picture,
+               picture.width(), picture.height());
+    fNestLevel += 1;
+    this->INHERITED::drawPicture(picture);
+    fNestLevel -= 1;
+    this->dump(kDrawPicture_Verb, NULL, "endPicture(%p) %d:%d", &picture,
+               picture.width(), picture.height());
+}
+
+void SkDumpCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                                 const SkPoint vertices[], const SkPoint texs[],
+                                 const SkColor colors[], SkXfermode* xmode,
+                                 const uint16_t indices[], int indexCount,
+                                 const SkPaint& paint) {
+    this->dump(kDrawVertices_Verb, &paint, "drawVertices(%s [%d] %g %g ...)",
+               toString(vmode), vertexCount, SkScalarToFloat(vertices[0].fX),
+               SkScalarToFloat(vertices[0].fY));
+}
+
+void SkDumpCanvas::drawData(const void* data, size_t length) {
+//    this->dump(kDrawData_Verb, NULL, "drawData(%d)", length);
+    this->dump(kDrawData_Verb, NULL, "drawData(%d) %.*s", length,
+               SkMin32(length, 64), data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkFormatDumper::SkFormatDumper(void (*proc)(const char*, void*), void* refcon) {
+    fProc = proc;
+    fRefcon = refcon;
+}
+
+static void appendPtr(SkString* str, const void* ptr, const char name[]) {
+    if (ptr) {
+        str->appendf(" %s:%p", name, ptr);
+    }
+}
+
+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);
+        }
+    }
+}
+
+void SkFormatDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb,
+                          const char str[], const SkPaint* p) {
+    SkString msg, tab;
+    const int level = canvas->getNestLevel() + canvas->getSaveCount() - 1;
+    SkASSERT(level >= 0);
+    for (int i = 0; i < level; i++) {
+        tab.append("\t");
+    }
+    msg.printf("%s%s", tab.c_str(), str);
+
+    if (p) {
+        msg.appendf(" color:0x%08X flags:%X", p->getColor(), p->getFlags());
+        appendFlattenable(&msg, p->getShader(), "shader");
+        appendFlattenable(&msg, p->getXfermode(), "xfermode");
+        appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
+        appendFlattenable(&msg, p->getMaskFilter(), "maskFilter");
+        appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
+        appendFlattenable(&msg, p->getColorFilter(), "filter");
+
+        if (SkDumpCanvas::kDrawText_Verb == verb) {
+            msg.appendf(" textSize:%g", SkScalarToFloat(p->getTextSize()));
+            appendPtr(&msg, p->getTypeface(), "typeface");
+        }
+    }
+
+    fProc(msg.c_str(), fRefcon);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void dumpToDebugf(const char text[], void*) {
+    SkDebugf("%s\n", text);
+}
+
+SkDebugfDumper::SkDebugfDumper() : INHERITED(dumpToDebugf, NULL) {}
+
+
diff --git a/legacy/src/utils/SkInterpolator.cpp b/legacy/src/utils/SkInterpolator.cpp
new file mode 100644
index 0000000..d43792f
--- /dev/null
+++ b/legacy/src/utils/SkInterpolator.cpp
@@ -0,0 +1,332 @@
+
+/*
+ * 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.
+ */
+
+
+#include "SkInterpolator.h"
+#include "SkMath.h"
+#include "SkTSearch.h"
+
+SkInterpolatorBase::SkInterpolatorBase() {
+    fStorage    = NULL;
+    fTimes      = NULL;
+    SkDEBUGCODE(fTimesArray = NULL;)
+}
+
+SkInterpolatorBase::~SkInterpolatorBase() {
+    if (fStorage) {
+        sk_free(fStorage);
+    }
+}
+
+void SkInterpolatorBase::reset(int elemCount, int frameCount) {
+    fFlags = 0;
+    fElemCount = SkToU8(elemCount);
+    fFrameCount = SkToS16(frameCount);
+    fRepeat = SK_Scalar1;
+    if (fStorage) {
+        sk_free(fStorage);
+        fStorage = NULL;
+        fTimes = NULL;
+        SkDEBUGCODE(fTimesArray = NULL);
+    }
+}
+
+/*  Each value[] run is formated as:
+        <time (in msec)>
+        <blend>
+        <data[fElemCount]>
+
+    Totaling fElemCount+2 entries per keyframe
+*/
+
+bool SkInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const {
+    if (fFrameCount == 0) {
+        return false;
+    }
+
+    if (startTime) {
+        *startTime = fTimes[0].fTime;
+    }
+    if (endTime) {
+        *endTime = fTimes[fFrameCount - 1].fTime;
+    }
+    return true;
+}
+
+SkScalar SkInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime,
+                                  SkMSec nextTime, const SkScalar blend[4]) {
+    SkASSERT(time > prevTime && time < nextTime);
+
+    SkScalar t = SkScalarDiv((SkScalar)(time - prevTime),
+                             (SkScalar)(nextTime - prevTime));
+    return blend ?
+            SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t;
+}
+
+SkInterpolatorBase::Result SkInterpolatorBase::timeToT(SkMSec time, SkScalar* T,
+                                        int* indexPtr, SkBool* exactPtr) const {
+    SkASSERT(fFrameCount > 0);
+    Result  result = kNormal_Result;
+    if (fRepeat != SK_Scalar1) {
+        SkMSec startTime = 0, endTime = 0;  // initialize to avoid warning
+        this->getDuration(&startTime, &endTime);
+        SkMSec totalTime = endTime - startTime;
+        SkMSec offsetTime = time - startTime;
+        endTime = SkScalarMulFloor(fRepeat, totalTime);
+        if (offsetTime >= endTime) {
+            SkScalar fraction = SkScalarFraction(fRepeat);
+            offsetTime = fraction == 0 && fRepeat > 0 ? totalTime :
+                SkScalarMulFloor(fraction, totalTime);
+            result = kFreezeEnd_Result;
+        } else {
+            int mirror = fFlags & kMirror;
+            offsetTime = offsetTime % (totalTime << mirror);
+            if (offsetTime > totalTime) { // can only be true if fMirror is true
+                offsetTime = (totalTime << 1) - offsetTime;
+            }
+        }
+        time = offsetTime + startTime;
+    }
+
+    int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time,
+                                  sizeof(SkTimeCode));
+
+    bool    exact = true;
+
+    if (index < 0) {
+        index = ~index;
+        if (index == 0) {
+            result = kFreezeStart_Result;
+        } else if (index == fFrameCount) {
+            if (fFlags & kReset) {
+                index = 0;
+            } else {
+                index -= 1;
+            }
+            result = kFreezeEnd_Result;
+        } else {
+            exact = false;
+        }
+    }
+    SkASSERT(index < fFrameCount);
+    const SkTimeCode* nextTime = &fTimes[index];
+    SkMSec   nextT = nextTime[0].fTime;
+    if (exact) {
+        *T = 0;
+    } else {
+        SkMSec prevT = nextTime[-1].fTime;
+        *T = ComputeRelativeT(time, prevT, nextT, nextTime[-1].fBlend);
+    }
+    *indexPtr = index;
+    *exactPtr = exact;
+    return result;
+}
+
+
+SkInterpolator::SkInterpolator() {
+    INHERITED::reset(0, 0);
+    fValues = NULL;
+    SkDEBUGCODE(fScalarsArray = NULL;)
+}
+
+SkInterpolator::SkInterpolator(int elemCount, int frameCount) {
+    SkASSERT(elemCount > 0);
+    this->reset(elemCount, frameCount);
+}
+
+void SkInterpolator::reset(int elemCount, int frameCount) {
+    INHERITED::reset(elemCount, frameCount);
+    fStorage = sk_malloc_throw((sizeof(SkScalar) * elemCount +
+                                sizeof(SkTimeCode)) * frameCount);
+    fTimes = (SkTimeCode*) fStorage;
+    fValues = (SkScalar*) ((char*) fStorage + sizeof(SkTimeCode) * frameCount);
+#ifdef SK_DEBUG
+    fTimesArray = (SkTimeCode(*)[10]) fTimes;
+    fScalarsArray = (SkScalar(*)[10]) fValues;
+#endif
+}
+
+#define SK_Fixed1Third      (SK_Fixed1/3)
+#define SK_Fixed2Third      (SK_Fixed1*2/3)
+
+static const SkScalar gIdentityBlend[4] = {
+#ifdef SK_SCALAR_IS_FLOAT
+    0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f
+#else
+    SK_Fixed1Third, SK_Fixed1Third, SK_Fixed2Third, SK_Fixed2Third
+#endif
+};
+
+bool SkInterpolator::setKeyFrame(int index, SkMSec time,
+                            const SkScalar values[], const SkScalar blend[4]) {
+    SkASSERT(values != NULL);
+    
+    if (blend == NULL) {
+        blend = gIdentityBlend;
+    }
+
+    bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time,
+                                               sizeof(SkTimeCode));
+    SkASSERT(success);
+    if (success) {
+        SkTimeCode* timeCode = &fTimes[index];
+        timeCode->fTime = time;
+        memcpy(timeCode->fBlend, blend, sizeof(timeCode->fBlend));
+        SkScalar* dst = &fValues[fElemCount * index];
+        memcpy(dst, values, fElemCount * sizeof(SkScalar));
+    }
+    return success;
+}
+
+SkInterpolator::Result SkInterpolator::timeToValues(SkMSec time,
+                                                    SkScalar values[]) const {
+    SkScalar T;
+    int index;
+    SkBool exact;
+    Result result = timeToT(time, &T, &index, &exact);
+    if (values) {
+        const SkScalar* nextSrc = &fValues[index * fElemCount];
+
+        if (exact) {
+            memcpy(values, nextSrc, fElemCount * sizeof(SkScalar));
+        } else {
+            SkASSERT(index > 0);
+
+            const SkScalar* prevSrc = nextSrc - fElemCount;
+
+            for (int i = fElemCount - 1; i >= 0; --i) {
+                values[i] = SkScalarInterp(prevSrc[i], nextSrc[i], T);
+            }
+        }
+    }
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef int Dot14;
+#define Dot14_ONE       (1 << 14)
+#define Dot14_HALF      (1 << 13)
+
+#define Dot14ToFloat(x) ((x) / 16384.f)
+
+static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) {
+    return (a * b + Dot14_HALF) >> 14;
+}
+
+static inline Dot14 eval_cubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) {
+    return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t);
+}
+
+static inline Dot14 pin_and_convert(SkScalar x) {
+    if (x <= 0) {
+        return 0;
+    }
+    if (x >= SK_Scalar1) {
+        return Dot14_ONE;
+    }
+    return SkScalarToFixed(x) >> 2;
+}
+
+SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by,
+                           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
+    //  t^3 -> 3b - 3c + 1
+    Dot14 A = 3*b;
+    Dot14 B = 3*(c - 2*b);
+    Dot14 C = 3*(b - c) + Dot14_ONE;
+
+    // Now search for a t value given x
+    Dot14   t = Dot14_HALF;
+    Dot14   dt = Dot14_HALF;
+    for (int i = 0; i < 13; i++) {
+        dt >>= 1;
+        Dot14 guess = eval_cubic(t, A, B, C);
+        if (x < guess) {
+            t -= dt;
+        } else {
+            t += dt;
+        }
+    }
+    
+    // Now we have t, so compute the coeff for Y and evaluate
+    b = pin_and_convert(by);
+    c = pin_and_convert(cy);
+    A = 3*b;
+    B = 3*(c - 2*b);
+    C = 3*(b - c) + Dot14_ONE;
+    return SkFixedToScalar(eval_cubic(t, A, B, C) << 2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#ifdef SK_SUPPORT_UNITTEST
+    static SkScalar* iset(SkScalar array[3], int a, int b, int c) {
+        array[0] = SkIntToScalar(a);
+        array[1] = SkIntToScalar(b);
+        array[2] = SkIntToScalar(c);
+        return array;
+    }
+#endif
+
+void SkInterpolator::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+    SkInterpolator  inter(3, 2);
+    SkScalar        v1[3], v2[3], v[3], vv[3];
+    Result          result;
+
+    inter.setKeyFrame(0, 100, iset(v1, 10, 20, 30), 0);
+    inter.setKeyFrame(1, 200, iset(v2, 110, 220, 330));
+
+    result = inter.timeToValues(0, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(99, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(100, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(200, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(201, v);
+    SkASSERT(result == kFreezeEnd_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(150, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, iset(vv, 60, 120, 180), sizeof(v)) == 0);
+
+    result = inter.timeToValues(125, v);
+    SkASSERT(result == kNormal_Result);
+    result = inter.timeToValues(175, v);
+    SkASSERT(result == kNormal_Result);
+#endif
+}
+
+#endif
+
diff --git a/legacy/src/utils/SkJSON.cpp b/legacy/src/utils/SkJSON.cpp
new file mode 100644
index 0000000..c55d464
--- /dev/null
+++ b/legacy/src/utils/SkJSON.cpp
@@ -0,0 +1,637 @@
+/*
+ * 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 "SkJSON.h"
+#include "SkString.h"
+
+#ifdef SK_DEBUG
+//    #define TRACE_SKJSON_LEAKS
+#endif
+
+#ifdef TRACE_SKJSON_LEAKS
+    static int gStringCount;
+    static int gSlotCount;
+    static int gObjectCount;
+    static int gArrayCount;
+    #define LEAK_CODE(code) code
+#else
+    #define LEAK_CODE(code)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static char* alloc_string(size_t len) {
+    LEAK_CODE(SkDebugf(" string[%d]\n", gStringCount++);)
+    char* str = (char*)sk_malloc_throw(len + 1);
+    str[len] = 0;
+    return str;
+}
+
+static char* dup_string(const char src[]) {
+    if (NULL == src) {
+        return NULL;
+    }
+    size_t len = strlen(src);
+    char* dst = alloc_string(len);
+    memcpy(dst, src, len);
+    return dst;
+}
+
+static void free_string(char* str) {
+    if (str) {
+        sk_free(str);
+        LEAK_CODE(SkASSERT(gStringCount > 0); SkDebugf("~string[%d]\n", --gStringCount);)
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkJSON::Object::Slot {
+    Slot(const char name[], Type type) {
+        LEAK_CODE(SkDebugf(" slot[%d]\n", gSlotCount++);)
+        SkASSERT(name);
+
+        fNext = NULL;
+        
+        size_t len = strlen(name);
+        // extra 1 for str[0] which stores the type
+        char* str = alloc_string(1 + len);
+        str[0] = (char)type;
+        // 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 {
+        Object* fObject;
+        Array*  fArray;
+        char*   fString;
+        int32_t fInt;
+        float   fFloat;
+        bool    fBool;
+    } fValue;
+};
+
+SkJSON::Object::Slot::~Slot() {
+    free_string(fName);
+    switch (this->type()) {
+        case kObject:
+            delete fValue.fObject;
+            break;
+        case kArray:
+            delete fValue.fArray;
+            break;
+        case kString:
+            free_string(fValue.fString);
+            break;
+        default:
+            break;
+    }
+    LEAK_CODE(SkASSERT(gSlotCount > 0); SkDebugf("~slot[%d]\n", --gSlotCount);)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkJSON::Object::Iter::Iter(const Object& obj) : fSlot(obj.fHead) {}
+
+bool SkJSON::Object::Iter::done() const {
+    return NULL == fSlot;
+}
+
+void SkJSON::Object::Iter::next() {
+    SkASSERT(fSlot);
+    fSlot = fSlot->fNext;
+}
+
+SkJSON::Type SkJSON::Object::Iter::type() const {
+    SkASSERT(fSlot);
+    return fSlot->type();
+}
+
+const char* SkJSON::Object::Iter::name() const {
+    SkASSERT(fSlot);
+    return fSlot->name();
+}
+
+SkJSON::Object* SkJSON::Object::Iter::objectValue() const {
+    SkASSERT(fSlot);
+    SkASSERT(kObject == fSlot->type());
+    return fSlot->fValue.fObject;
+}
+
+SkJSON::Array* SkJSON::Object::Iter::arrayValue() const {
+    SkASSERT(fSlot);
+    SkASSERT(kArray == fSlot->type());
+    return fSlot->fValue.fArray;
+}
+
+const char* SkJSON::Object::Iter::stringValue() const {
+    SkASSERT(fSlot);
+    SkASSERT(kString == fSlot->type());
+    return fSlot->fValue.fString;
+}
+
+int32_t SkJSON::Object::Iter::intValue() const {
+    SkASSERT(fSlot);
+    SkASSERT(kInt == fSlot->type());
+    return fSlot->fValue.fInt;
+}
+
+float SkJSON::Object::Iter::floatValue() const {
+    SkASSERT(fSlot);
+    SkASSERT(kFloat == fSlot->type());
+    return fSlot->fValue.fFloat;
+}
+
+bool SkJSON::Object::Iter::boolValue() const {
+    SkASSERT(fSlot);
+    SkASSERT(kBool == fSlot->type());
+    return fSlot->fValue.fBool;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkJSON::Object::Object() : fHead(NULL), fTail(NULL) {
+    LEAK_CODE(SkDebugf(" object[%d]\n", gObjectCount++);)
+}
+
+SkJSON::Object::Object(const Object& other) : fHead(NULL), fTail(NULL) {
+    LEAK_CODE(SkDebugf(" object[%d]\n", gObjectCount++);)
+
+    Iter iter(other);
+    while (!iter.done()) {
+        switch (iter.type()) {
+            case kObject:
+                this->addObject(iter.name(), new Object(*iter.objectValue()));
+                break;
+            case kArray:
+                this->addArray(iter.name(), new Array(*iter.arrayValue()));
+                break;
+            case kString:
+                this->addString(iter.name(), dup_string(iter.stringValue()));
+                break;
+            case kInt:
+                this->addInt(iter.name(), iter.intValue());
+                break;
+            case kFloat:
+                this->addFloat(iter.name(), iter.floatValue());
+                break;
+            case kBool:
+                this->addBool(iter.name(), iter.boolValue());
+                break;
+        }
+        iter.next();
+    }
+}
+
+SkJSON::Object::~Object() {
+    Slot* slot = fHead;
+    while (slot) {
+        Slot* next = slot->fNext;
+        delete slot;
+        slot = next;
+    }
+    LEAK_CODE(SkASSERT(gObjectCount > 0); SkDebugf("~object[%d]\n", --gObjectCount);)
+}
+
+int SkJSON::Object::count() const {
+    int n = 0;
+    for (const Slot* slot = fHead; slot; slot = slot->fNext) {
+        n += 1;
+    }
+    return n;
+}
+
+SkJSON::Object::Slot* SkJSON::Object::addSlot(Slot* slot) {
+    SkASSERT(NULL == slot->fNext);
+    if (NULL == fHead) {
+        SkASSERT(NULL == fTail);
+        fHead = fTail = slot;
+    } else {
+        SkASSERT(fTail);
+        SkASSERT(NULL == fTail->fNext);
+        fTail->fNext = slot;
+        fTail = slot;
+    }
+    return slot;
+}
+
+void SkJSON::Object::addObject(const char name[], SkJSON::Object* value) {
+    this->addSlot(new Slot(name, kObject))->fValue.fObject = value;
+}
+
+void SkJSON::Object::addArray(const char name[], SkJSON::Array* value) {
+    this->addSlot(new Slot(name, kArray))->fValue.fArray = value;
+}
+
+void SkJSON::Object::addString(const char name[], const char value[]) {
+    this->addSlot(new Slot(name, kString))->fValue.fString = dup_string(value);
+}
+
+void SkJSON::Object::addInt(const char name[], int32_t value) {
+    this->addSlot(new Slot(name, kInt))->fValue.fInt = value;
+}
+
+void SkJSON::Object::addFloat(const char name[], float value) {
+    this->addSlot(new Slot(name, kFloat))->fValue.fFloat = value;
+}
+
+void SkJSON::Object::addBool(const char name[], bool value) {
+    this->addSlot(new Slot(name, kBool))->fValue.fBool = value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkJSON::Object::Slot* SkJSON::Object::findSlot(const char name[],
+                                                     Type t) const {
+    for (const Slot* slot = fHead; slot; slot = slot->fNext) {
+        if (t == slot->type() && !strcmp(slot->name(), name)) {
+            return slot;
+        }
+    }
+    return NULL;
+}
+
+bool SkJSON::Object::find(const char name[], Type t) const {
+    return this->findSlot(name, t) != NULL;
+}
+
+bool SkJSON::Object::findObject(const char name[], SkJSON::Object** value) const {
+    const Slot* slot = this->findSlot(name, kObject);
+    if (slot) {
+        if (value) {
+            *value = slot->fValue.fObject;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkJSON::Object::findArray(const char name[], SkJSON::Array** value) const {
+    const Slot* slot = this->findSlot(name, kArray);
+    if (slot) {
+        if (value) {
+            *value = slot->fValue.fArray;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkJSON::Object::findString(const char name[], SkString* value) const {
+    const Slot* slot = this->findSlot(name, kString);
+    if (slot) {
+        if (value) {
+            value->set(slot->fValue.fString);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkJSON::Object::findInt(const char name[], int32_t* value) const {
+    const Slot* slot = this->findSlot(name, kInt);
+    if (slot) {
+        if (value) {
+            *value = slot->fValue.fInt;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkJSON::Object::findFloat(const char name[], float* value) const {
+    const Slot* slot = this->findSlot(name, kFloat);
+    if (slot) {
+        if (value) {
+            *value = slot->fValue.fFloat;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkJSON::Object::findBool(const char name[], bool* value) const {
+    const Slot* slot = this->findSlot(name, kBool);
+    if (slot) {
+        if (value) {
+            *value = slot->fValue.fBool;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkJSON::Object::remove(const char name[], Type t) {
+    SkDEBUGCODE(int count = this->count();)
+    Slot* prev = NULL;
+    Slot* slot = fHead;
+    while (slot) {
+        Slot* next = slot->fNext;
+        if (t == slot->type() && !strcmp(slot->name(), name)) {
+            if (prev) {
+                SkASSERT(fHead != slot);
+                prev->fNext = next;
+            } else {
+                SkASSERT(fHead == slot);
+                fHead = next;
+            }
+            if (fTail == slot) {
+                fTail = prev;
+            }
+            delete slot;
+            SkASSERT(count - 1 == this->count());
+            return true;
+        }
+        prev = slot;
+        slot = next;
+    }
+    SkASSERT(count == this->count());
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void tabForLevel(int level) {
+    for (int i = 0; i < level; ++i) {
+        SkDebugf("    ");
+    }
+}
+
+void SkJSON::Object::toDebugf() const {
+    SkDebugf("{\n");
+    this->dumpLevel(0);
+    SkDebugf("}\n");
+}
+
+void SkJSON::Object::dumpLevel(int level) const {
+    for (Slot* slot = fHead; slot; slot = slot->fNext) {
+        Type t = slot->type();        
+        tabForLevel(level + 1);
+        SkDebugf("\"%s\" : ", slot->name());
+        switch (slot->type()) {
+            case kObject:
+                if (slot->fValue.fObject) {
+                    SkDebugf("{\n");
+                    slot->fValue.fObject->dumpLevel(level + 1);
+                    tabForLevel(level + 1);
+                    SkDebugf("}");
+                } else {
+                    SkDebugf("null");
+                }
+                break;
+            case kArray:
+                if (slot->fValue.fArray) {
+                    SkDebugf("[");
+                    slot->fValue.fArray->dumpLevel(level + 1);
+                    SkDebugf("]");
+                } else {
+                    SkDebugf("null");
+                }
+                break;
+            case kString:
+                SkDebugf("\"%s\"", slot->fValue.fString);
+                break;
+            case kInt:
+                SkDebugf("%d", slot->fValue.fInt);
+                break;
+            case kFloat:
+                SkDebugf("%g", slot->fValue.fFloat);
+                break;
+            case kBool:
+                SkDebugf("%s", slot->fValue.fBool ? "true" : "false");
+                break;
+            default:
+                SkASSERT(!"how did I get here");
+                break;
+        }
+        if (slot->fNext) {
+            SkDebugf(",");
+        }
+        SkDebugf("\n");
+    }
+}
+
+void SkJSON::Array::dumpLevel(int level) const {
+    if (0 == fCount) {
+        return;
+    }
+    int last = fCount - 1;
+
+    switch (this->type()) {
+        case kObject: {
+            SkDebugf("\n");
+            for (int i = 0; i <= last; ++i) {
+                Object* obj = fArray.fObjects[i];
+                tabForLevel(level + 1);
+                if (obj) {
+                    SkDebugf("{\n");
+                    obj->dumpLevel(level + 1);
+                    tabForLevel(level + 1);
+                    SkDebugf(i < last ? "}," : "}");
+                } else {
+                    SkDebugf(i < last ? "null," : "null");
+                }
+                SkDebugf("\n");
+            }
+        } break;
+        case kArray: {
+            SkDebugf("\n");
+            for (int i = 0; i <= last; ++i) {
+                Array* array = fArray.fArrays[i];
+                tabForLevel(level + 1);
+                if (array) {
+                    SkDebugf("[");
+                    array->dumpLevel(level + 1);
+                    tabForLevel(level + 1);
+                    SkDebugf(i < last ? "]," : "]");
+                } else {
+                    SkDebugf(i < last ? "null," : "null");
+                }
+                SkDebugf("\n");
+            }
+        } break;
+        case kString: {
+            for (int i = 0; i < last; ++i) {
+                const char* str = fArray.fStrings[i];
+                SkDebugf(str ? " \"%s\"," : " null,", str);
+            }
+            const char* str = fArray.fStrings[last];
+            SkDebugf(str ? " \"%s\" " : " null ", str);
+        } break;
+        case kInt: {
+            for (int i = 0; i < last; ++i) {
+                SkDebugf(" %d,", fArray.fInts[i]);
+            }
+            SkDebugf(" %d ", fArray.fInts[last]);
+        } break;
+        case kFloat: {
+            for (int i = 0; i < last; ++i) {
+                SkDebugf(" %g,", fArray.fFloats[i]);
+            }
+            SkDebugf(" %g ", fArray.fFloats[last]);
+        } break;
+        case kBool: {
+            for (int i = 0; i < last; ++i) {
+                SkDebugf(" %s,", fArray.fBools[i] ? "true" : "false");
+            }
+            SkDebugf(" %s ", fArray.fInts[last] ? "true" : "false");
+        } break;
+        default:
+            SkASSERT(!"unsupported array type");
+            break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t gBytesPerType[] = {
+    sizeof(SkJSON::Object*),
+    sizeof(SkJSON::Array*),
+    sizeof(char*),
+    sizeof(int32_t),
+    sizeof(float),
+    sizeof(bool)
+};
+
+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));
+}
+
+static const DupProc gDupProcs[] = {
+    dup_object,             // Object
+    dup_array,              // Array
+    (DupProc)dup_string,    // String
+    NULL,                   // int
+    NULL,                   // float
+    NULL,                   // bool
+};
+
+void SkJSON::Array::init(Type type, int count, const void* src) {
+    LEAK_CODE(SkDebugf(" array[%d]\n", gArrayCount++);)
+
+    SkASSERT((unsigned)type < SK_ARRAY_COUNT(gBytesPerType));
+
+    if (count < 0) {
+        count = 0;
+    }
+    size_t size = count * gBytesPerType[type];
+
+    fCount = count;
+    fType = type;
+    fArray.fVoids = sk_malloc_throw(size);
+    if (src) {
+        DupProc proc = gDupProcs[fType];
+        if (!proc) {
+            memcpy(fArray.fVoids, src, size);
+        } else {
+            void** srcPtr = (void**)src;
+            void** dstPtr = (void**)fArray.fVoids;
+            for (int i = 0; i < fCount; ++i) {
+                dstPtr[i] = proc(srcPtr[i]);
+            }
+        }
+    } else {
+        sk_bzero(fArray.fVoids, size);
+    }
+}
+
+SkJSON::Array::Array(Type type, int count) {
+    this->init(type, count, NULL);
+}
+
+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);
+}
+
+SkJSON::Array::Array(const bool values[], int count) {
+    this->init(kBool, count, values);
+}
+
+SkJSON::Array::Array(const Array& other) {
+    this->init(other.type(), other.count(), other.fArray.fVoids);
+}
+
+typedef void (*FreeProc)(void*);
+
+static void free_object(void* obj) {
+    delete (SkJSON::Object*)obj;
+}
+
+static void free_array(void* array) {
+    delete (SkJSON::Array*)array;
+}
+
+static const FreeProc gFreeProcs[] = {
+    free_object,            // Object
+    free_array,             // Array
+    (FreeProc)free_string,  // String
+    NULL,                   // int
+    NULL,                   // float
+    NULL,                   // bool
+};
+
+SkJSON::Array::~Array() {
+    FreeProc proc = gFreeProcs[fType];
+    if (proc) {
+        void** ptr = (void**)fArray.fVoids;
+        for (int i = 0; i < fCount; ++i) {
+            proc(ptr[i]);
+        }
+    }
+    sk_free(fArray.fVoids);
+
+    LEAK_CODE(SkASSERT(gArrayCount > 0); SkDebugf("~array[%d]\n", --gArrayCount);)
+}
+
+void SkJSON::Array::setObject(int index, Object* object) {
+    SkASSERT((unsigned)index < (unsigned)fCount);
+    Object*& prev = fArray.fObjects[index];
+    if (prev != object) {
+        delete prev;
+        prev = object;
+    }
+}
+
+void SkJSON::Array::setArray(int index, Array* array) {
+    SkASSERT((unsigned)index < (unsigned)fCount);
+    Array*& prev = fArray.fArrays[index];
+    if (prev != array) {
+        delete prev;
+        prev = array;
+    }
+}
+
+void SkJSON::Array::setString(int index, const char str[]) {
+    SkASSERT((unsigned)index < (unsigned)fCount);
+    char*& prev = fArray.fStrings[index];
+    if (prev != str) {
+        free_string(prev);
+        prev = dup_string(str);
+    }
+}
+
+
+
diff --git a/legacy/src/utils/SkLayer.cpp b/legacy/src/utils/SkLayer.cpp
new file mode 100644
index 0000000..aaca786
--- /dev/null
+++ b/legacy/src/utils/SkLayer.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 "SkLayer.h"
+#include "SkCanvas.h"
+
+//#define DEBUG_DRAW_LAYER_BOUNDS
+//#define DEBUG_TRACK_NEW_DELETE
+
+#ifdef DEBUG_TRACK_NEW_DELETE
+    static int gLayerAllocCount;
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkLayer::SkLayer() {
+    fParent = NULL;
+    m_opacity = SK_Scalar1;
+    m_size.set(0, 0);
+    m_position.set(0, 0);
+    m_anchorPoint.set(SK_ScalarHalf, SK_ScalarHalf);
+
+    fMatrix.reset();
+    fChildrenMatrix.reset();
+    fFlags = 0;
+
+#ifdef DEBUG_TRACK_NEW_DELETE
+    gLayerAllocCount += 1;
+    SkDebugf("SkLayer new:    %d\n", gLayerAllocCount);
+#endif
+}
+
+SkLayer::SkLayer(const SkLayer& src) : INHERITED() {
+    fParent = NULL;
+    m_opacity = src.m_opacity;
+    m_size = src.m_size;
+    m_position = src.m_position;
+    m_anchorPoint = src.m_anchorPoint;
+
+    fMatrix = src.fMatrix;
+    fChildrenMatrix = src.fChildrenMatrix;
+    fFlags = src.fFlags;
+
+#ifdef DEBUG_TRACK_NEW_DELETE
+    gLayerAllocCount += 1;
+    SkDebugf("SkLayer copy:   %d\n", gLayerAllocCount);
+#endif
+}
+
+SkLayer::~SkLayer() {
+    this->removeChildren();
+
+#ifdef DEBUG_TRACK_NEW_DELETE
+    gLayerAllocCount -= 1;
+    SkDebugf("SkLayer delete: %d\n", gLayerAllocCount);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkLayer::isInheritFromRootTransform() const {
+    return (fFlags & kInheritFromRootTransform_Flag) != 0;
+}
+
+void SkLayer::setInheritFromRootTransform(bool doInherit) {
+    if (doInherit) {
+        fFlags |= kInheritFromRootTransform_Flag;
+    } else {
+        fFlags &= ~kInheritFromRootTransform_Flag;
+    }
+}
+
+void SkLayer::setMatrix(const SkMatrix& matrix) {
+    fMatrix = matrix;
+}
+
+void SkLayer::setChildrenMatrix(const SkMatrix& matrix) {
+    fChildrenMatrix = matrix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkLayer::countChildren() const {
+    return m_children.count();
+}
+
+SkLayer* SkLayer::getChild(int index) const {
+    if ((unsigned)index < (unsigned)m_children.count()) {
+        SkASSERT(m_children[index]->fParent == this);
+        return m_children[index];
+    }
+    return NULL;
+}
+
+SkLayer* SkLayer::addChild(SkLayer* child) {
+    SkASSERT(this != child);
+    child->ref();
+    child->detachFromParent();
+    SkASSERT(child->fParent == NULL);
+    child->fParent = this;
+
+    *m_children.append() = child;
+    return child;
+}
+
+void SkLayer::detachFromParent() {
+    if (fParent) {
+        int index = fParent->m_children.find(this);
+        SkASSERT(index >= 0);
+        fParent->m_children.remove(index);
+        fParent = NULL;
+        this->unref();  // this call might delete us
+    }
+}
+
+void SkLayer::removeChildren() {
+    int count = m_children.count();
+    for (int i = 0; i < count; i++) {
+        SkLayer* child = m_children[i];
+        SkASSERT(child->fParent == this);
+        child->fParent = NULL;  // in case it has more than one owner
+        child->unref();
+    }
+    m_children.reset();
+}
+
+SkLayer* SkLayer::getRootLayer() const {
+    const SkLayer* root = this;
+    while (root->fParent != NULL) {
+        root = root->fParent;
+    }
+    return const_cast<SkLayer*>(root);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkLayer::getLocalTransform(SkMatrix* matrix) const {
+    matrix->setTranslate(m_position.fX, m_position.fY);
+
+    SkScalar tx = SkScalarMul(m_anchorPoint.fX, m_size.width());
+    SkScalar ty = SkScalarMul(m_anchorPoint.fY, m_size.height());
+    matrix->preTranslate(tx, ty);
+    matrix->preConcat(this->getMatrix());
+    matrix->preTranslate(-tx, -ty);
+}
+
+void SkLayer::localToGlobal(SkMatrix* matrix) const {
+    this->getLocalTransform(matrix);
+
+    if (this->isInheritFromRootTransform()) {
+        matrix->postConcat(this->getRootLayer()->getMatrix());
+        return;
+    }
+
+    const SkLayer* layer = this;
+    while (layer->fParent != NULL) {
+        layer = layer->fParent;
+
+        SkMatrix tmp;
+        layer->getLocalTransform(&tmp);
+        tmp.preConcat(layer->getChildrenMatrix());
+        matrix->postConcat(tmp);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkLayer::onDraw(SkCanvas*, SkScalar opacity) {
+//    SkDebugf("----- no onDraw for %p\n", this);
+}
+
+#include "SkString.h"
+
+void SkLayer::draw(SkCanvas* canvas, SkScalar opacity) {
+#if 0
+    SkString str1, str2;
+ //   this->getMatrix().toDumpString(&str1);
+ //   this->getChildrenMatrix().toDumpString(&str2);
+    SkDebugf("--- drawlayer %p opacity %g size [%g %g] pos [%g %g] matrix %s children %s\n",
+             this, opacity * this->getOpacity(), m_size.width(), m_size.height(),
+             m_position.fX, m_position.fY, str1.c_str(), str2.c_str());
+#endif
+
+    opacity = SkScalarMul(opacity, this->getOpacity());
+    if (opacity <= 0) {
+//        SkDebugf("---- abort drawing %p opacity %g\n", this, opacity);
+        return;
+    }
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    // apply our local transform
+    {
+        SkMatrix tmp;
+        this->getLocalTransform(&tmp);
+        if (this->isInheritFromRootTransform()) {
+            // should we also apply the root's childrenMatrix?
+            canvas->setMatrix(getRootLayer()->getMatrix());
+        }
+        canvas->concat(tmp);
+    }
+
+    this->onDraw(canvas, opacity);
+
+#ifdef DEBUG_DRAW_LAYER_BOUNDS
+    {
+        SkRect r = SkRect::MakeSize(this->getSize());
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(SkIntToScalar(2));
+        p.setColor(0xFFFF44DD);
+        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);
+    }
+#endif
+
+    int count = this->countChildren();
+    if (count > 0) {
+        canvas->concat(this->getChildrenMatrix());
+        for (int i = 0; i < count; i++) {
+            this->getChild(i)->draw(canvas, opacity);
+        }
+    }
+}
+
diff --git a/legacy/src/utils/SkMatrix44.cpp b/legacy/src/utils/SkMatrix44.cpp
new file mode 100644
index 0000000..f00e399
--- /dev/null
+++ b/legacy/src/utils/SkMatrix44.cpp
@@ -0,0 +1,393 @@
+
+/*
+ * 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 "SkMatrix44.h"
+
+SkMatrix44::SkMatrix44() {
+    this->setIdentity();
+}
+
+SkMatrix44::SkMatrix44(const SkMatrix44& src) {
+    memcpy(this, &src, sizeof(src));
+}
+
+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];
+}
+
+void SkMatrix44::set(int row, int col, const SkMScalar& value) {
+    SkASSERT(row <= 3 && row >= 0);
+    SkASSERT(col <= 3 && col >= 0);
+    fMat[col][row] = value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::asColMajorf(float dst[]) const {
+    const SkMScalar* src = &fMat[0][0];
+#ifdef SK_MSCALAR_IS_DOUBLE
+    for (int i = 0; i < 16; ++i) {
+        dst[i] = SkMScalarToFloat(src[i]);
+    }
+#else
+    memcpy(dst, src, 16 * sizeof(float));
+#endif
+}
+
+void SkMatrix44::asColMajord(double dst[]) const {
+    const SkMScalar* src = &fMat[0][0];
+#ifdef SK_MSCALAR_IS_DOUBLE
+    memcpy(dst, src, 16 * sizeof(double));
+#else
+    for (int i = 0; i < 16; ++i) {
+        dst[i] = SkMScalarToDouble(src[i]);
+    }
+#endif
+}
+
+void SkMatrix44::asRowMajorf(float dst[]) const {
+    const SkMScalar* src = &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;
+    }
+}
+
+void SkMatrix44::asRowMajord(double dst[]) const {
+    const SkMScalar* src = &fMat[0][0];
+    for (int i = 0; i < 4; ++i) {
+        dst[0] = SkMScalarToDouble(src[0]);
+        dst[4] = SkMScalarToDouble(src[1]);
+        dst[8] = SkMScalarToDouble(src[2]);
+        dst[12] = SkMScalarToDouble(src[3]);
+        src += 4;
+        dst += 1;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::setIdentity() {
+    sk_bzero(fMat, sizeof(fMat));
+    fMat[0][0] = fMat[1][1] = fMat[2][2] = fMat[3][3] = 1;
+}
+
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::setTranslate(SkMScalar tx, SkMScalar ty, SkMScalar tz) {
+    this->setIdentity();
+    fMat[3][0] = tx;
+    fMat[3][1] = ty;
+    fMat[3][2] = tz;
+    fMat[3][3] = 1;
+}
+
+void SkMatrix44::preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
+    SkMatrix44 mat;
+    mat.setTranslate(dx, dy, dz);
+    this->preConcat(mat);
+}
+
+void SkMatrix44::postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
+    fMat[3][0] += dx;
+    fMat[3][1] += dy;
+    fMat[3][2] += dz;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
+    sk_bzero(fMat, sizeof(fMat));
+    fMat[0][0] = sx;
+    fMat[1][1] = sy;
+    fMat[2][2] = sz;
+    fMat[3][3] = 1;
+}
+
+void SkMatrix44::preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
+    SkMatrix44 tmp;
+    tmp.setScale(sx, sy, sz);
+    this->preConcat(tmp);
+}
+
+void SkMatrix44::postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
+    for (int i = 0; i < 4; i++) {
+        fMat[i][0] *= sx;
+        fMat[i][1] *= sy;
+        fMat[i][2] *= sz;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+            this->setIdentity();
+            return;
+        }
+        double scale = 1 / sqrt(len2);
+        x = SkDoubleToMScalar(x * scale);
+        y = SkDoubleToMScalar(y * scale);
+        z = SkDoubleToMScalar(z * scale);
+    }
+    this->setRotateAboutUnit(x, y, z, radians);
+}
+
+void SkMatrix44::setRotateAboutUnit(SkMScalar x, SkMScalar y, SkMScalar z,
+                                    SkMScalar radians) {
+    double c = cos(radians);
+    double s = sin(radians);
+    double C = 1 - c;
+    double xs = x * s;
+    double ys = y * s;
+    double zs = z * s;
+    double xC = x * C;
+    double yC = y * C;
+    double zC = z * C;
+    double xyC = x * yC;
+    double yzC = y * zC;
+    double zxC = z * xC;
+
+    // if you're looking at wikipedia, remember that we're column major.
+    this->set3x3(SkDoubleToMScalar(x * xC + c),     // scale x
+                 SkDoubleToMScalar(xyC + zs),       // skew x
+                 SkDoubleToMScalar(zxC - ys),       // trans x
+
+                 SkDoubleToMScalar(xyC - zs),       // skew y
+                 SkDoubleToMScalar(y * yC + c),     // scale y
+                 SkDoubleToMScalar(yzC + xs),       // trans y
+
+                 SkDoubleToMScalar(zxC + ys),       // persp x
+                 SkDoubleToMScalar(yzC - xs),       // persp y
+                 SkDoubleToMScalar(z * zC + c));    // persp 2
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::setConcat(const SkMatrix44& a, const SkMatrix44& b) {
+    SkMScalar result[4][4];
+    for (int i = 0; i < 4; i++) {
+        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];
+            }
+            result[j][i] = SkDoubleToMScalar(value);
+        }
+    }
+    memcpy(fMat, result, sizeof(result));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline SkMScalar det2x2(double m00, double m01, double m10, double m11) {
+    return SkDoubleToMScalar(m00 * m11 - m10 * m01);
+}
+
+static inline double det3x3(double m00, double m01, double m02,
+                            double m10, double m11, double m12,
+                            double m20, double m21, double m22) {
+    return  m00 * det2x2(m11, m12, m21, m22) -
+    m10 * det2x2(m01, m02, m21, m22) +
+    m20 * det2x2(m01, m02, m11, m12);
+}
+
+/** We always perform the calculation in doubles, to avoid prematurely losing
+    precision along the way. This relies on the compiler automatically
+    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]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// just picked a small value. not sure how to pick the "right" one
+#define TOO_SMALL_FOR_DETERMINANT   (1.e-8)
+
+static inline double dabs(double x) {
+    if (x < 0) {
+        x = -x;
+    }
+    return x;
+}
+
+bool SkMatrix44::invert(SkMatrix44* inverse) const {
+    double det = this->determinant();
+    if (dabs(det) < TOO_SMALL_FOR_DETERMINANT) {
+        return false;
+    }
+    if (NULL == inverse) {
+        return true;
+    }
+
+    // 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];
+
+    double tmp[4][4];
+
+    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);
+        }
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::map(const SkScalar src[4], SkScalar dst[4]) const {
+    SkScalar result[4];
+    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] = SkMScalarToScalar(value);
+    }
+    memcpy(dst, result, sizeof(result));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::dump() const {
+    static const char* format =
+        "[%g %g %g %g][%g %g %g %g][%g %g %g %g][%g %g %g %g]\n";
+#if 0
+    SkDebugf(format,
+             fMat[0][0], fMat[1][0], fMat[2][0], fMat[3][0],
+             fMat[0][1], fMat[1][1], fMat[2][1], fMat[3][1],
+             fMat[0][2], fMat[1][2], fMat[2][2], fMat[3][2],
+             fMat[0][3], fMat[1][3], fMat[2][3], fMat[3][3]);
+#else
+    SkDebugf(format,
+             fMat[0][0], fMat[0][1], fMat[0][2], fMat[0][3],
+             fMat[1][0], fMat[1][1], fMat[1][2], fMat[1][3],
+             fMat[2][0], fMat[2][1], fMat[2][2], fMat[2][3],
+             fMat[3][0], fMat[3][1], fMat[3][2], fMat[3][3]);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void initFromMatrix(SkMScalar dst[4][4], const SkMatrix& src) {
+    sk_bzero(dst, 16 * sizeof(SkMScalar));
+    dst[0][0] = SkScalarToMScalar(src[SkMatrix::kMScaleX]);
+    dst[1][0] = SkScalarToMScalar(src[SkMatrix::kMSkewX]);
+    dst[3][0] = SkScalarToMScalar(src[SkMatrix::kMTransX]);
+    dst[0][1] = SkScalarToMScalar(src[SkMatrix::kMSkewY]);
+    dst[1][1] = SkScalarToMScalar(src[SkMatrix::kMScaleY]);
+    dst[3][1] = SkScalarToMScalar(src[SkMatrix::kMTransY]);
+    dst[2][2] = dst[3][3] = 1;
+}
+
+SkMatrix44::SkMatrix44(const SkMatrix& src) {
+    initFromMatrix(fMat, src);
+}
+
+SkMatrix44& SkMatrix44::operator=(const SkMatrix& src) {
+    initFromMatrix(fMat, src);
+    return *this;
+}
+
+SkMatrix44::operator SkMatrix() const {
+    SkMatrix dst;
+    dst.reset();    // setup our perspective correctly for identity
+
+    dst[SkMatrix::kMScaleX]  = SkMScalarToScalar(fMat[0][0]);
+    dst[SkMatrix::kMSkewX]  = SkMScalarToScalar(fMat[1][0]);
+    dst[SkMatrix::kMTransX] = SkMScalarToScalar(fMat[3][0]);
+
+    dst[SkMatrix::kMSkewY]  = SkMScalarToScalar(fMat[0][1]);
+    dst[SkMatrix::kMScaleY] = SkMScalarToScalar(fMat[1][1]);
+    dst[SkMatrix::kMTransY] = SkMScalarToScalar(fMat[3][1]);
+
+    return dst;
+}
diff --git a/legacy/src/utils/SkMeshUtils.cpp b/legacy/src/utils/SkMeshUtils.cpp
new file mode 100644
index 0000000..f7af383
--- /dev/null
+++ b/legacy/src/utils/SkMeshUtils.cpp
@@ -0,0 +1,103 @@
+
+/*
+ * 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 "SkMeshUtils.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+SkMeshIndices::SkMeshIndices() {
+    sk_bzero(this, sizeof(*this));
+}
+
+SkMeshIndices::~SkMeshIndices() {
+    sk_free(fStorage);
+}
+
+bool SkMeshIndices::init(SkPoint tex[], uint16_t indices[],
+                         int texW, int texH, int rows, int cols) {
+    if (rows < 2 || cols < 2) {
+        sk_free(fStorage);
+        fStorage = NULL;
+        fTex = NULL;
+        fIndices = NULL;
+        fTexCount = fIndexCount = 0;
+        return false;
+    }
+
+    sk_free(fStorage);
+    fStorage = NULL;
+
+    fTexCount = rows * cols;
+    rows -= 1;
+    cols -= 1;
+    fIndexCount = rows * cols * 6;
+
+    if (tex) {
+        fTex = tex;
+        fIndices = indices;
+    } else {
+        fStorage = sk_malloc_throw(fTexCount * sizeof(SkPoint) +
+                                   fIndexCount * sizeof(uint16_t));
+        fTex = (SkPoint*)fStorage;
+        fIndices = (uint16_t*)(fTex + fTexCount);
+    }
+
+    // compute the indices
+    {
+        uint16_t* idx = fIndices;
+        int index = 0;
+        for (int y = 0; y < cols; y++) {
+            for (int x = 0; x < rows; x++) {
+                *idx++ = index;
+                *idx++ = index + rows + 1;
+                *idx++ = index + 1;
+                
+                *idx++ = index + 1;
+                *idx++ = index + rows + 1;
+                *idx++ = index + rows + 2;
+                
+                index += 1;
+            }
+            index += 1;
+        }
+    }
+
+    // compute texture coordinates
+    {
+        SkPoint* tex = fTex;
+        const SkScalar dx = SkIntToScalar(texW) / rows;
+        const SkScalar dy = SkIntToScalar(texH) / cols;
+        for (int y = 0; y <= cols; y++) {
+            for (int x = 0; x <= rows; x++) {
+                tex->set(x*dx, y*dy);
+                tex += 1;
+            }
+        }
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkShader.h"
+
+void SkMeshUtils::Draw(SkCanvas* canvas, const SkBitmap& bitmap,
+                       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,
+                                         SkShader::kClamp_TileMode,
+                                         SkShader::kClamp_TileMode))->unref();
+        canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
+                             rows * cols, verts, idx.tex(), colors, NULL,
+                             idx.indices(), idx.indexCount(), p);
+    }
+}
+
diff --git a/legacy/src/utils/SkNWayCanvas.cpp b/legacy/src/utils/SkNWayCanvas.cpp
new file mode 100644
index 0000000..9f6ebc6
--- /dev/null
+++ b/legacy/src/utils/SkNWayCanvas.cpp
@@ -0,0 +1,290 @@
+
+/*
+ * 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 "SkNWayCanvas.h"
+
+SkNWayCanvas::SkNWayCanvas(int width, int height) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, width, height);
+    this->setBitmapDevice(bm);
+}
+
+SkNWayCanvas::~SkNWayCanvas() {
+    this->removeAll();
+}
+
+void SkNWayCanvas::addCanvas(SkCanvas* canvas) {
+    if (canvas) {
+        canvas->ref();
+        *fList.append() = canvas;
+    }
+}
+
+void SkNWayCanvas::removeCanvas(SkCanvas* canvas) {
+    int index = fList.find(canvas);
+    if (index >= 0) {
+        canvas->unref();
+        fList.removeShuffle(index);
+    }
+}
+
+void SkNWayCanvas::removeAll() {
+    fList.unrefAll();
+    fList.reset();
+}
+
+///////////////////////////////////////////////////////////////////////////
+// These are forwarded to the N canvases we're referencing
+
+class SkNWayCanvas::Iter {
+public:
+    Iter(const SkTDArray<SkCanvas*>& list) : fList(list) {
+        fIndex = 0;
+    }
+    bool next() {
+        if (fIndex < fList.count()) {
+            fCanvas = fList[fIndex++];
+            return true;
+        }
+        return false;
+    }
+    SkCanvas* operator->() { return fCanvas; }
+
+private:
+    const SkTDArray<SkCanvas*>& fList;
+    int fIndex;
+    SkCanvas* fCanvas;
+};
+
+int SkNWayCanvas::save(SaveFlags flags) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->save(flags);
+    }
+    return this->INHERITED::save(flags);
+}
+
+int SkNWayCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                                    SaveFlags flags) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->saveLayer(bounds, paint, flags);
+    }
+    return this->INHERITED::saveLayer(bounds, paint, flags);
+}
+
+void SkNWayCanvas::restore() {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->restore();
+    }
+    this->INHERITED::restore();
+}
+
+bool SkNWayCanvas::translate(SkScalar dx, SkScalar dy) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->translate(dx, dy);
+    }
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkNWayCanvas::scale(SkScalar sx, SkScalar sy) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->scale(sx, sy);
+    }
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkNWayCanvas::rotate(SkScalar degrees) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->rotate(degrees);
+    }
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkNWayCanvas::skew(SkScalar sx, SkScalar sy) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->skew(sx, sy);
+    }
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkNWayCanvas::concat(const SkMatrix& matrix) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->concat(matrix);
+    }
+    return this->INHERITED::concat(matrix);
+}
+
+void SkNWayCanvas::setMatrix(const SkMatrix& matrix) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->setMatrix(matrix);
+    }
+    this->INHERITED::setMatrix(matrix);
+}
+
+bool SkNWayCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->clipRect(rect, op, doAA);
+    }
+    return this->INHERITED::clipRect(rect, op, doAA);
+}
+
+bool SkNWayCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->clipPath(path, op, doAA);
+    }
+    return this->INHERITED::clipPath(path, op, doAA);
+}
+
+bool SkNWayCanvas::clipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->clipRegion(deviceRgn, op);
+    }
+    return this->INHERITED::clipRegion(deviceRgn, op);
+}
+
+void SkNWayCanvas::drawPaint(const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawPaint(paint);
+    }
+}
+
+void SkNWayCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                        const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawPoints(mode, count, pts, paint);
+    }
+}
+
+void SkNWayCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawRect(rect, paint);
+    }
+}
+
+void SkNWayCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawPath(path, paint);
+    }
+}
+
+void SkNWayCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                              const SkPaint* paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawBitmap(bitmap, x, y, paint);
+    }
+}
+
+void SkNWayCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                                  const SkRect& dst, const SkPaint* paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawBitmapRect(bitmap, src, dst, paint);
+    }
+}
+
+void SkNWayCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                    const SkPaint* paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawBitmapMatrix(bitmap, m, paint);
+    }
+}
+
+void SkNWayCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                              const SkPaint* paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawSprite(bitmap, x, y, paint);
+    }
+}
+
+void SkNWayCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+                            SkScalar y, const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawText(text, byteLength, x, y, paint);
+    }
+}
+
+void SkNWayCanvas::drawPosText(const void* text, size_t byteLength,
+                               const SkPoint pos[], const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawPosText(text, byteLength, pos, paint);
+    }
+}
+
+void SkNWayCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                const SkScalar xpos[], SkScalar constY,
+                                const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawPosTextH(text, byteLength, xpos, constY, paint);
+    }
+}
+
+void SkNWayCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                                  const SkPath& path, const SkMatrix* matrix,
+                                  const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawTextOnPath(text, byteLength, path, matrix, paint);
+    }
+}
+
+void SkNWayCanvas::drawPicture(SkPicture& picture) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawPicture(picture);
+    }
+}
+
+void SkNWayCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                          const SkPoint vertices[], const SkPoint texs[],
+                          const SkColor colors[], SkXfermode* xmode,
+                          const uint16_t indices[], int indexCount,
+                          const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
+                           indices, indexCount, paint);
+    }
+}
+
+SkBounder* SkNWayCanvas::setBounder(SkBounder* bounder) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->setBounder(bounder);
+    }
+    return this->INHERITED::setBounder(bounder);
+}
+
+SkDrawFilter* SkNWayCanvas::setDrawFilter(SkDrawFilter* filter) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->setDrawFilter(filter);
+    }
+    return this->INHERITED::setDrawFilter(filter);
+}
+
+
diff --git a/legacy/src/utils/SkNinePatch.cpp b/legacy/src/utils/SkNinePatch.cpp
new file mode 100644
index 0000000..26ae8eb
--- /dev/null
+++ b/legacy/src/utils/SkNinePatch.cpp
@@ -0,0 +1,335 @@
+
+/*
+ * 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 "SkNinePatch.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+
+static const uint16_t g3x3Indices[] = {
+    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
+};
+
+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;
+    }
+    return indices - startIndices;
+}
+
+// Computes the delta between vertices along a single axis
+static SkScalar computeVertexDelta(bool isStretchyVertex,
+                                   SkScalar currentVertex,
+                                   SkScalar prevVertex,
+                                   SkScalar stretchFactor) {
+    // the standard delta between vertices if no stretching is required
+    SkScalar delta = currentVertex - prevVertex;
+
+    // if the stretch factor is negative or zero we need to shrink the 9-patch
+    // to fit within the target bounds.  This means that we will eliminate all
+    // stretchy areas and scale the fixed areas to fit within the target bounds.
+    if (stretchFactor <= 0) {
+        if (isStretchyVertex)
+            delta = 0; // collapse stretchable areas
+        else
+            delta = SkScalarMul(delta, -stretchFactor); // scale fixed areas
+    // if the stretch factor is positive then we use the standard delta for
+    // fixed and scale the stretchable areas to fill the target bounds.
+    } else if (isStretchyVertex) {
+        delta = SkScalarMul(delta, stretchFactor);
+    }
+
+    return delta;
+}
+
+static void fillRow(SkPoint verts[], SkPoint texs[],
+                    const SkScalar vy, const SkScalar ty,
+                    const SkRect& bounds, const int32_t xDivs[], int numXDivs,
+                    const SkScalar stretchX, int width) {
+    SkScalar vx = bounds.fLeft;
+    verts->set(vx, vy); verts++;
+    texs->set(0, ty); texs++;
+
+    SkScalar prev = 0;
+    for (int x = 0; x < numXDivs; x++) {
+
+        const SkScalar tx = SkIntToScalar(xDivs[x]);
+        vx += computeVertexDelta(x & 1, tx, prev, stretchX);
+        prev = tx;
+
+        verts->set(vx, vy); verts++;
+        texs->set(tx, ty); texs++;
+    }
+    verts->set(bounds.fRight, vy); verts++;
+    texs->set(SkIntToScalar(width), ty); texs++;
+}
+
+struct Mesh {
+    const SkPoint*  fVerts;
+    const SkPoint*  fTexs;
+    const SkColor*  fColors;
+    const uint16_t* fIndices;
+};
+
+void SkNinePatch::DrawMesh(SkCanvas* canvas, const SkRect& bounds,
+                           const SkBitmap& bitmap,
+                           const int32_t xDivs[], int numXDivs,
+                           const int32_t yDivs[], int numYDivs,
+                           const SkPaint* paint) {
+    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;
+        int zeros = 0;
+        for (i = 0; i < numYDivs && yDivs[i] == 0; i++) {
+            zeros += 1;
+        }
+        numYDivs -= zeros;
+        yDivs += zeros;
+        for (i = numYDivs - 1; i >= 0 && yDivs[i] == bitmap.height(); --i) {
+            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++) {
+            SkDebugf("--- xdivs[%d] %d\n", i, xDivs[i]);
+        }
+        for (i = 0; i < numYDivs; i++) {
+            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) {
+            stretchSize += xDivs[i] - xDivs[i-1];
+        }
+        const SkScalar fixed = SkIntToScalar(bitmap.width() - stretchSize);
+        if (bounds.width() >= fixed)
+            stretchX = (bounds.width() - fixed) / stretchSize;
+        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) {
+            stretchSize += yDivs[i] - yDivs[i-1];
+        }
+        const SkScalar fixed = SkIntToScalar(bitmap.height() - stretchSize);
+        if (bounds.height() >= fixed)
+            stretchY = (bounds.height() - fixed) / stretchSize;
+        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(),
+             SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
+             numXDivs + 1, numYDivs + 1,
+             SkScalarToFloat(stretchX), SkScalarToFloat(stretchY));
+#endif
+
+    const int vCount = (numXDivs + 2) * (numYDivs + 2);
+    // number of celss * 2 (tris per cell) * 3 (verts per tri)
+    const int indexCount = (numXDivs + 1) * (numYDivs + 1) * 2 * 3;
+    // allocate 2 times, one for verts, one for texs, plus indices
+    SkAutoMalloc storage(vCount * sizeof(SkPoint) * 2 +
+                         indexCount * sizeof(uint16_t));
+    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;
+    } else {
+        SkDEBUGCODE(int n =) fillIndices(indices, numXDivs + 1, numYDivs + 1);
+        SkASSERT(n == indexCount);
+        mesh.fIndices = indices;
+    }
+    
+    SkScalar vy = bounds.fTop;
+    fillRow(verts, texs, vy, 0, bounds, xDivs, numXDivs,
+            stretchX, bitmap.width());
+    verts += numXDivs + 2;
+    texs += numXDivs + 2;
+    for (int y = 0; y < numYDivs; y++) {
+        const SkScalar ty = SkIntToScalar(yDivs[y]);
+        if (stretchY >= 0) {
+            if (y & 1) {
+                vy += stretchY;
+            } else {
+                vy += ty;
+            }
+        } else {    // shrink fixed sections, and collaps stretchy sections
+            if (y & 1) {
+                ;// do nothing
+            } else {
+                vy += SkScalarMul(ty, -stretchY);
+            }
+        }
+        fillRow(verts, texs, vy, ty, bounds, xDivs, numXDivs,
+                stretchX, bitmap.width());
+        verts += numXDivs + 2;
+        texs += numXDivs + 2;
+    }
+    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);
+    SkPaint p;
+    if (paint) {
+        p = *paint;
+    }
+    p.setShader(shader)->unref();
+    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vCount,
+                         mesh.fVerts, mesh.fTexs, mesh.fColors, NULL,
+                         mesh.fIndices, indexCount, p);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawNineViaRects(SkCanvas* canvas, const SkRect& dst,
+                             const SkBitmap& bitmap, const SkIRect& margins,
+                             const SkPaint* paint) {
+    const int32_t srcX[4] = {
+        0, margins.fLeft, bitmap.width() - margins.fRight, bitmap.width()
+    };
+    const int32_t srcY[4] = {
+        0, margins.fTop, bitmap.height() - margins.fBottom, bitmap.height()
+    };
+    SkScalar dstX[4] = {
+        dst.fLeft, dst.fLeft + SkIntToScalar(margins.fLeft),
+        dst.fRight - SkIntToScalar(margins.fRight), dst.fRight
+    };
+    SkScalar dstY[4] = {
+        dst.fTop, dst.fTop + SkIntToScalar(margins.fTop),
+        dst.fBottom - SkIntToScalar(margins.fBottom), dst.fBottom
+    };
+
+    if (dstX[1] > dstX[2]) {
+        dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * SkIntToScalar(margins.fLeft) /
+            (SkIntToScalar(margins.fLeft) + SkIntToScalar(margins.fRight));
+        dstX[2] = dstX[1];
+    }
+
+    if (dstY[1] > dstY[2]) {
+        dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * SkIntToScalar(margins.fTop) /
+            (SkIntToScalar(margins.fTop) + SkIntToScalar(margins.fBottom));
+        dstY[2] = dstY[1];
+    }
+
+    SkIRect s;
+    SkRect  d;
+    for (int y = 0; y < 3; y++) {
+        s.fTop = srcY[y];
+        s.fBottom = srcY[y+1];
+        d.fTop = dstY[y];
+        d.fBottom = dstY[y+1];
+        for (int x = 0; x < 3; x++) {
+            s.fLeft = srcX[x];
+            s.fRight = srcX[x+1];
+            d.fLeft = dstX[x];
+            d.fRight = dstX[x+1];
+            canvas->drawBitmapRect(bitmap, &s, d, paint);
+        }
+    }
+}
+
+void SkNinePatch::DrawNine(SkCanvas* canvas, const SkRect& bounds,
+                           const SkBitmap& bitmap, const SkIRect& margins,
+                           const SkPaint* paint) {
+    /** Our vertices code has numerical precision problems if the transformed
+     coordinates land directly on a 1/2 pixel boundary. To work around that
+     for now, we only take the vertices case if we are in opengl. Also,
+     when not in GL, the vertices impl is slower (more math) than calling
+     the viaRects code.
+     */
+    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;
+        yDivs[1] = bitmap.height() - margins.fBottom;
+
+        if (xDivs[0] > xDivs[1]) {
+            xDivs[0] = bitmap.width() * margins.fLeft /
+                (margins.fLeft + margins.fRight);
+            xDivs[1] = xDivs[0];
+        }
+        if (yDivs[0] > yDivs[1]) {
+            yDivs[0] = bitmap.height() * margins.fTop /
+                (margins.fTop + margins.fBottom);
+            yDivs[1] = yDivs[0];
+        }
+        
+        SkNinePatch::DrawMesh(canvas, bounds, bitmap,
+                              xDivs, 2, yDivs, 2, paint);
+    } else {
+        drawNineViaRects(canvas, bounds, bitmap, margins, paint);
+    }
+}
diff --git a/legacy/src/utils/SkOSFile.cpp b/legacy/src/utils/SkOSFile.cpp
new file mode 100644
index 0000000..7c2b024
--- /dev/null
+++ b/legacy/src/utils/SkOSFile.cpp
@@ -0,0 +1,230 @@
+
+/*
+ * 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 "SkOSFile.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+static uint16_t* concat_to_16(const char src[], const char suffix[])
+{
+    size_t  i, len = strlen(src);
+    size_t  len2 = 3 + (suffix ? strlen(suffix) : 0);
+    uint16_t* dst = (uint16_t*)sk_malloc_throw((len + len2) * sizeof(uint16_t));
+
+    for (i = 0; i < len; i++)
+        dst[i] = src[i];
+
+    if (i > 0 && dst[i-1] != '/')
+        dst[i++] = '/';
+    dst[i++] = '*';
+
+    if (suffix)
+    {
+        while (*suffix)
+            dst[i++] = *suffix++;
+    }
+    dst[i] = 0;
+    SkASSERT(i + 1 <= len + len2);
+
+    return dst;
+}
+
+SkUTF16_Str::SkUTF16_Str(const char src[])
+{
+    size_t  len = strlen(src);
+
+    fStr = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
+    size_t i;
+    for (i = 0; i < len; i++)
+        fStr[i] = src[i];
+    fStr[i] = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkOSFile::Iter::Iter() : fHandle(0), fPath16(NULL)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fHandle(0), fPath16(NULL)
+{
+    this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+    sk_free(fPath16);
+    if (fHandle)
+        ::FindClose(fHandle);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+    if (fHandle)
+    {
+        ::FindClose(fHandle);
+        fHandle = 0;
+    }
+    if (NULL == path)
+        path = "";
+
+    sk_free(fPath16);
+    fPath16 = concat_to_16(path, suffix);
+}
+
+static bool is_magic_dir(const uint16_t dir[])
+{
+    // return true for "." and ".."
+    return dir[0] == '.' && (dir[1] == 0 || dir[1] == '.' && dir[2] == 0);
+}
+
+static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir)
+{
+    WIN32_FIND_DATAW    data;
+
+    if (NULL == dataPtr)
+    {
+        if (::FindNextFileW(handle, &data))
+            dataPtr = &data;
+        else
+            return false;
+    }
+
+    for (;;)
+    {
+        if (getDir)
+        {
+            if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !is_magic_dir((uint16_t*)dataPtr->cFileName))
+                break;
+        }
+        else
+        {
+            if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                break;
+        }
+        if (!::FindNextFileW(handle, dataPtr))
+            return false;
+    }
+    // if we get here, we've found a file/dir
+    if (name)
+        name->setUTF16((uint16_t*)dataPtr->cFileName);
+    return true;
+}
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+    WIN32_FIND_DATAW    data;
+    WIN32_FIND_DATAW*   dataPtr = NULL;
+
+    if (fHandle == 0)   // our first time
+    {
+        if (fPath16 == NULL || *fPath16 == 0)    // check for no path
+            return false;
+
+        fHandle = ::FindFirstFileW((LPCWSTR)fPath16, &data);
+        if (fHandle != 0 && fHandle != (HANDLE)~0)
+            dataPtr = &data;
+    }
+    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)
+
+#if 0
+OSStatus FSPathMakeRef (
+   const UInt8 * path,
+   FSRef * ref,
+   Boolean * isDirectory
+);
+#endif
+
+SkOSFile::Iter::Iter() : fDIR(0)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fDIR(0)
+{
+    this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+    if (fDIR)
+        ::closedir(fDIR);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+    if (fDIR)
+    {
+        ::closedir(fDIR);
+        fDIR = 0;
+    }
+
+    fPath.set(path);
+    if (path)
+    {
+        fDIR = ::opendir(path);
+        fSuffix.set(suffix);
+    }
+    else
+        fSuffix.reset();
+}
+
+// returns true if suffix is empty, or if str ends with suffix
+static bool issuffixfor(const SkString& suffix, const char str[])
+{
+    size_t  suffixLen = suffix.size();
+    size_t  strLen = strlen(str);
+
+    return  strLen >= suffixLen &&
+            memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0;
+}
+
+#include <sys/stat.h>
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+    if (fDIR)
+    {
+        dirent* entry;
+
+        while ((entry = ::readdir(fDIR)) != NULL)
+        {
+            struct stat s;
+            SkString    str(fPath);
+
+            if (!str.endsWith("/") && !str.endsWith("\\"))
+                str.append("/");
+            str.append(entry->d_name);
+
+            if (0 == stat(str.c_str(), &s))
+            {
+                if (getDir)
+                {
+                    if (s.st_mode & S_IFDIR)
+                        break;
+                }
+                else
+                {
+                    if (!(s.st_mode & S_IFDIR) && issuffixfor(fSuffix, entry->d_name))
+                        break;
+                }
+            }
+        }
+        if (entry)  // we broke out with a file
+        {
+            if (name)
+                name->set(entry->d_name);
+            return true;
+        }
+    }
+    return false;
+}
+
+#endif
+
diff --git a/legacy/src/utils/SkParse.cpp b/legacy/src/utils/SkParse.cpp
new file mode 100644
index 0000000..cb265c3
--- /dev/null
+++ b/legacy/src/utils/SkParse.cpp
@@ -0,0 +1,338 @@
+
+/*
+ * 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 "SkParse.h"
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static inline bool is_digit(int c)
+{
+    return is_between(c, '0', '9');
+}
+
+static inline bool is_sep(int c)
+{
+    return is_ws(c) || c == ',' || c == ';';
+}
+
+static int to_hex(int c)
+{
+    if (is_digit(c))
+        return c - '0';
+
+    c |= 0x20;  // make us lower-case
+    if (is_between(c, 'a', 'f'))
+        return c + 10 - 'a';
+    else
+        return -1;
+}
+
+static inline bool is_hex(int c)
+{
+    return to_hex(c) >= 0;
+}
+
+static const char* skip_ws(const char str[])
+{
+    SkASSERT(str);
+    while (is_ws(*str))
+        str++;
+    return str;
+}
+
+static const char* skip_sep(const char str[])
+{
+    SkASSERT(str);
+    while (is_sep(*str))
+        str++;
+    return str;
+}
+
+int SkParse::Count(const char str[]) 
+{
+    char c;
+    int count = 0;
+    goto skipLeading;
+    do {
+        count++;
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (is_sep(c) == false);
+skipLeading:
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (is_sep(c));
+    } while (true);
+goHome:
+    return count;
+}
+
+int SkParse::Count(const char str[], char separator) 
+{
+    char c;
+    int count = 0;
+    goto skipLeading;
+    do {
+        count++;
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (c != separator);
+skipLeading:
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (c == separator);
+    } while (true);
+goHome:
+    return count;
+}
+
+const char* SkParse::FindHex(const char str[], uint32_t* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    if (!is_hex(*str))
+        return NULL;
+
+    uint32_t n = 0;
+    int max_digits = 8;
+    int digit;
+
+    while ((digit = to_hex(*str)) >= 0)
+    {
+        if (--max_digits < 0)
+            return NULL;
+        n = (n << 4) | digit;
+        str += 1;
+    }
+
+    if (*str == 0 || is_ws(*str))
+    {
+        if (value)
+            *value = n;
+        return str;
+    }
+    return NULL;
+}
+
+const char* SkParse::FindS32(const char str[], int32_t* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str))
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        str += 1;
+    }
+    if (value)
+        *value = (n ^ sign) - sign;
+    return str;
+}
+
+const char* SkParse::FindMSec(const char str[], SkMSec* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str))
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        str += 1;
+    }
+    int remaining10s = 3;
+    if (*str == '.') {
+        str++;
+        while (is_digit(*str))
+        {
+            n = 10*n + *str - '0';
+            str += 1;
+            if (--remaining10s == 0)
+                break;
+        }
+    }
+    while (--remaining10s >= 0)
+        n *= 10;
+    if (value)
+        *value = (n ^ sign) - sign;
+    return str;
+}
+
+const char* SkParse::FindScalar(const char str[], SkScalar* value) {
+    SkASSERT(str);
+    str = skip_ws(str);
+#ifdef SK_SCALAR_IS_FLOAT
+    char* stop;
+    float v = (float)strtod(str, &stop);
+    if (str == stop) {
+        return NULL;
+    }
+    if (value) {
+        *value = v;
+    }
+    return stop;
+#else
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str) && *str != '.')
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        if (n > 0x7FFF)
+            return NULL;
+        str += 1;
+    }
+    n <<= 16;
+
+    if (*str == '.')
+    {
+        static const int gFractions[] = { (1 << 24)  / 10, (1 << 24)  / 100, (1 << 24)  / 1000, 
+            (1 << 24)  / 10000, (1 << 24)  / 100000 };
+        str += 1;
+        int d = 0;
+        const int* fraction = gFractions;
+        const int* end = &fraction[SK_ARRAY_COUNT(gFractions)];
+        while (is_digit(*str) && fraction < end)
+            d += (*str++ - '0') * *fraction++;
+        d += 0x80; // round
+        n += d >> 8;
+    }
+    while (is_digit(*str))
+        str += 1;
+    if (value)
+    {
+        n = (n ^ sign) - sign;  // apply the sign
+        *value = SkFixedToScalar(n);
+    }
+#endif
+    return str;
+}
+
+const char* SkParse::FindScalars(const char str[], SkScalar value[], int count)
+{
+    SkASSERT(count >= 0);
+
+    if (count > 0)
+    {
+        for (;;)
+        {
+            str = SkParse::FindScalar(str, value);
+            if (--count == 0 || str == NULL)
+                break;
+
+            // keep going
+            str = skip_sep(str);
+            if (value)
+                value += 1;
+        }
+    }
+    return str;
+}
+
+static bool lookup_str(const char str[], const char** table, int count)
+{
+    while (--count >= 0)
+        if (!strcmp(str, table[count]))
+            return true;
+    return false;
+}
+
+bool SkParse::FindBool(const char str[], bool* value)
+{
+    static const char* gYes[] = { "yes", "1", "true" };
+    static const char* gNo[] = { "no", "0", "false" };
+
+    if (lookup_str(str, gYes, SK_ARRAY_COUNT(gYes)))
+    {
+        if (value) *value = true;
+        return true;
+    }
+    else if (lookup_str(str, gNo, SK_ARRAY_COUNT(gNo)))
+    {
+        if (value) *value = false;
+        return true;
+    }
+    return false;
+}
+
+int SkParse::FindList(const char target[], const char list[])
+{
+    size_t  len = strlen(target);
+    int     index = 0;
+
+    for (;;)
+    {
+        const char* end = strchr(list, ',');
+        size_t      entryLen;
+
+        if (end == NULL) // last entry
+            entryLen = strlen(list);
+        else
+            entryLen = end - list;
+
+        if (entryLen == len && memcmp(target, list, len) == 0)
+            return index;
+        if (end == NULL)
+            break;
+
+        list = end + 1; // skip the ','
+        index += 1;
+    }
+    return -1;
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkParse::UnitTest() 
+{
+    // !!! additional parse tests go here
+    SkParse::TestColor();
+}
+#endif
diff --git a/legacy/src/utils/SkParseColor.cpp b/legacy/src/utils/SkParseColor.cpp
new file mode 100644
index 0000000..db47aae
--- /dev/null
+++ b/legacy/src/utils/SkParseColor.cpp
@@ -0,0 +1,540 @@
+
+/*
+ * 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 "SkParse.h"
+
+#ifdef SK_DEBUG
+#include "SkString.h"
+
+    // compress names 6 chars per long (packed 5 bits/char )
+        // note: little advantage to splitting chars across longs, since 3 longs at 2 unused bits each
+        // allow for one additional split char (vs. the 18 unsplit chars in the three longs)
+    // use extra two bits to represent:
+        // 00 : final 6 (or fewer) chars (if 'a' is 0x01, zero could have special meaning)
+        // 01 : not final 6 chars
+        // 10 : color
+        // 11 : unused, except as debugging sentinal? (could be -1 for easier test)
+    // !!! the bit to end the word (last) is at the low bit for binary search
+    // lookup first character in offset for quick start
+        // offset is 27-entry table of bytes(?) that trims linear search to at most 21 entries ('d')
+    // shift match into long; set bit 30 if it all doesn't fit
+    // while longs don't match, march forward
+        // if they do match, and bit 30 is set, advance match, clearing bit 30 if
+        // final chars, and advance to next test
+        // if they do match, and bit 30 is clear, get next long (color) and return it
+    // stop at lookup of first char + 1
+static const struct SkNameRGB {
+    const char* name;
+    int rgb;
+} colorNames[] = {
+    { "aliceblue",            0xF0F8FF },
+    { "antiquewhite",         0xFAEBD7 },
+    { "aqua",                 0x00FFFF },
+    { "aquamarine",           0x7FFFD4 },
+    { "azure",                0xF0FFFF },
+    { "beige",                0xF5F5DC },
+    { "bisque",               0xFFE4C4 },
+    { "black",                0x000000 },
+    { "blanchedalmond",       0xFFEBCD },
+    { "blue",                 0x0000FF },
+    { "blueviolet",           0x8A2BE2 },
+    { "brown",                0xA52A2A },
+    { "burlywood",            0xDEB887 },
+    { "cadetblue",            0x5F9EA0 },
+    { "chartreuse",           0x7FFF00 },
+    { "chocolate",            0xD2691E },
+    { "coral",                0xFF7F50 },
+    { "cornflowerblue",       0x6495ED },
+    { "cornsilk",             0xFFF8DC },
+    { "crimson",              0xDC143C },
+    { "cyan",                 0x00FFFF },
+    { "darkblue",             0x00008B },
+    { "darkcyan",             0x008B8B },
+    { "darkgoldenrod",        0xB8860B },
+    { "darkgray",             0xA9A9A9 },
+    { "darkgreen",            0x006400 },
+    { "darkkhaki",            0xBDB76B },
+    { "darkmagenta",          0x8B008B },
+    { "darkolivegreen",       0x556B2F },
+    { "darkorange",           0xFF8C00 },
+    { "darkorchid",           0x9932CC },
+    { "darkred",              0x8B0000 },
+    { "darksalmon",           0xE9967A },
+    { "darkseagreen",         0x8FBC8F },
+    { "darkslateblue",        0x483D8B },
+    { "darkslategray",        0x2F4F4F },
+    { "darkturquoise",        0x00CED1 },
+    { "darkviolet",           0x9400D3 },
+    { "deeppink",             0xFF1493 },
+    { "deepskyblue",          0x00BFFF },
+    { "dimgray",              0x696969 },
+    { "dodgerblue",           0x1E90FF },
+    { "firebrick",            0xB22222 },
+    { "floralwhite",          0xFFFAF0 },
+    { "forestgreen",          0x228B22 },
+    { "fuchsia",              0xFF00FF },
+    { "gainsboro",            0xDCDCDC },
+    { "ghostwhite",           0xF8F8FF },
+    { "gold",                 0xFFD700 },
+    { "goldenrod",            0xDAA520 },
+    { "gray",                 0x808080 },
+    { "green",                0x008000 },
+    { "greenyellow",          0xADFF2F },
+    { "honeydew",             0xF0FFF0 },
+    { "hotpink",              0xFF69B4 },
+    { "indianred",            0xCD5C5C },
+    { "indigo",               0x4B0082 },
+    { "ivory",                0xFFFFF0 },
+    { "khaki",                0xF0E68C },
+    { "lavender",             0xE6E6FA },
+    { "lavenderblush",        0xFFF0F5 },
+    { "lawngreen",            0x7CFC00 },
+    { "lemonchiffon",         0xFFFACD },
+    { "lightblue",            0xADD8E6 },
+    { "lightcoral",           0xF08080 },
+    { "lightcyan",            0xE0FFFF },
+    { "lightgoldenrodyellow", 0xFAFAD2 },
+    { "lightgreen",           0x90EE90 },
+    { "lightgrey",            0xD3D3D3 },
+    { "lightpink",            0xFFB6C1 },
+    { "lightsalmon",          0xFFA07A },
+    { "lightseagreen",        0x20B2AA },
+    { "lightskyblue",         0x87CEFA },
+    { "lightslategray",       0x778899 },
+    { "lightsteelblue",       0xB0C4DE },
+    { "lightyellow",          0xFFFFE0 },
+    { "lime",                 0x00FF00 },
+    { "limegreen",            0x32CD32 },
+    { "linen",                0xFAF0E6 },
+    { "magenta",              0xFF00FF },
+    { "maroon",               0x800000 },
+    { "mediumaquamarine",     0x66CDAA },
+    { "mediumblue",           0x0000CD },
+    { "mediumorchid",         0xBA55D3 },
+    { "mediumpurple",         0x9370DB },
+    { "mediumseagreen",       0x3CB371 },
+    { "mediumslateblue",      0x7B68EE },
+    { "mediumspringgreen",    0x00FA9A },
+    { "mediumturquoise",      0x48D1CC },
+    { "mediumvioletred",      0xC71585 },
+    { "midnightblue",         0x191970 },
+    { "mintcream",            0xF5FFFA },
+    { "mistyrose",            0xFFE4E1 },
+    { "moccasin",             0xFFE4B5 },
+    { "navajowhite",          0xFFDEAD },
+    { "navy",                 0x000080 },
+    { "oldlace",              0xFDF5E6 },
+    { "olive",                0x808000 },
+    { "olivedrab",            0x6B8E23 },
+    { "orange",               0xFFA500 },
+    { "orangered",            0xFF4500 },
+    { "orchid",               0xDA70D6 },
+    { "palegoldenrod",        0xEEE8AA },
+    { "palegreen",            0x98FB98 },
+    { "paleturquoise",        0xAFEEEE },
+    { "palevioletred",        0xDB7093 },
+    { "papayawhip",           0xFFEFD5 },
+    { "peachpuff",            0xFFDAB9 },
+    { "peru",                 0xCD853F },
+    { "pink",                 0xFFC0CB },
+    { "plum",                 0xDDA0DD },
+    { "powderblue",           0xB0E0E6 },
+    { "purple",               0x800080 },
+    { "red",                  0xFF0000 },
+    { "rosybrown",            0xBC8F8F },
+    { "royalblue",            0x4169E1 },
+    { "saddlebrown",          0x8B4513 },
+    { "salmon",               0xFA8072 },
+    { "sandybrown",           0xF4A460 },
+    { "seagreen",             0x2E8B57 },
+    { "seashell",             0xFFF5EE },
+    { "sienna",               0xA0522D },
+    { "silver",               0xC0C0C0 },
+    { "skyblue",              0x87CEEB },
+    { "slateblue",            0x6A5ACD },
+    { "slategray",            0x708090 },
+    { "snow",                 0xFFFAFA },
+    { "springgreen",          0x00FF7F },
+    { "steelblue",            0x4682B4 },
+    { "tan",                  0xD2B48C },
+    { "teal",                 0x008080 },
+    { "thistle",              0xD8BFD8 },
+    { "tomato",               0xFF6347 },
+    { "turquoise",            0x40E0D0 },
+    { "violet",               0xEE82EE },
+    { "wheat",                0xF5DEB3 },
+    { "white",                0xFFFFFF },
+    { "whitesmoke",           0xF5F5F5 },
+    { "yellow",               0xFFFF00 },
+    { "yellowgreen",          0x9ACD32 }
+};
+
+int colorNamesSize = sizeof(colorNames) / sizeof(colorNames[0]);
+
+#ifdef SK_SUPPORT_UNITTEST
+static void CreateTable() {
+    SkString comment;
+    size_t originalSize = 0;
+    int replacement = 0;
+    for (int index = 0; index < colorNamesSize; index++) {
+        SkNameRGB nameRGB =  colorNames[index];
+        const char* name = nameRGB.name;
+        size_t len = strlen(name);
+        originalSize += len + 9;
+        bool first = true;
+        bool last = false;
+        do {
+            int compressed = 0;
+            const char* start = name;
+            for (int chIndex = 0; chIndex < 6; chIndex++) {
+                compressed <<= 5;
+                compressed |= *name ? *name++ - 'a' + 1 : 0 ;
+            }
+            replacement += sizeof(int);
+            compressed <<= 1;
+            compressed |= 1;
+            if (first) {
+                compressed |= 0x80000000;
+                first = false;
+            }
+            if (len <= 6) { // last
+                compressed &= ~1;
+                last = true;
+            }
+            len -= 6;
+            SkDebugf("0x%08x, ", compressed);
+            comment.append(start, name - start);
+        } while (last == false);
+        replacement += sizeof(int);
+        SkDebugf("0x%08x, ", nameRGB.rgb);
+        SkDebugf("// %s\n", comment.c_str());
+        comment.reset();
+    }
+    SkDebugf("// original = %d : replacement = %d\n", originalSize, replacement);
+    SkASSERT(0); // always stop after creating table
+}
+#endif
+
+#endif
+
+static const unsigned int gColorNames[] = {
+0x85891945, 0x32a50000, 0x00f0f8ff, // aliceblue
+0x85d44c6b, 0x16e84d0a, 0x00faebd7, // antiquewhite
+0x86350800, 0x0000ffff, // aqua
+0x86350b43, 0x492e2800, 0x007fffd4, // aquamarine
+0x87559140, 0x00f0ffff, // azure
+0x88a93940, 0x00f5f5dc, // beige
+0x89338d4a, 0x00ffe4c4, // bisque
+0x89811ac0, 0x00000000, // black
+0x898170d1, 0x1481635f, 0x38800000, 0x00ffebcd, // blanchedalmond
+0x89952800, 0x000000ff, // blue
+0x89952d93, 0x3d85a000, 0x008a2be2, // blueviolet
+0x8a4fbb80, 0x00a52a2a, // brown
+0x8ab2666f, 0x3de40000, 0x00deb887, // burlywood
+0x8c242d05, 0x32a50000, 0x005f9ea0, // cadetblue
+0x8d019525, 0x16b32800, 0x007fff00, // chartreuse
+0x8d0f1bd9, 0x06850000, 0x00d2691e, // chocolate
+0x8df20b00, 0x00ff7f50, // coral
+0x8df27199, 0x3ee59099, 0x54a00000, 0x006495ed, // cornflowerblue
+0x8df274d3, 0x31600000, 0x00fff8dc, // cornsilk
+0x8e496cdf, 0x38000000, 0x00dc143c, // crimson
+0x8f217000, 0x0000ffff, // cyan
+0x90325899, 0x54a00000, 0x0000008b, // darkblue
+0x903258f3, 0x05c00000, 0x00008b8b, // darkcyan
+0x903259df, 0x3085749f, 0x10000000, 0x00b8860b, // darkgoldenrod
+0x903259e5, 0x07200000, 0x00a9a9a9, // darkgray
+0x903259e5, 0x14ae0000, 0x00006400, // darkgreen
+0x90325ad1, 0x05690000, 0x00bdb76b, // darkkhaki
+0x90325b43, 0x1caea040, 0x008b008b, // darkmagenta
+0x90325bd9, 0x26c53c8b, 0x15c00000, 0x00556b2f, // darkolivegreen
+0x90325be5, 0x05c72800, 0x00ff8c00, // darkorange
+0x90325be5, 0x0d092000, 0x009932cc, // darkorchid
+0x90325c8b, 0x10000000, 0x008b0000, // darkred
+0x90325cc3, 0x31af7000, 0x00e9967a, // darksalmon
+0x90325ccb, 0x04f2295c, 0x008fbc8f, // darkseagreen
+0x90325cd9, 0x0685132b, 0x14000000, 0x00483d8b, // darkslateblue
+0x90325cd9, 0x06853c83, 0x64000000, 0x002f4f4f, // darkslategray
+0x90325d2b, 0x4a357a67, 0x14000000, 0x0000ced1, // darkturquoise
+0x90325d93, 0x3d85a000, 0x009400d3, // darkviolet
+0x90a58413, 0x39600000, 0x00ff1493, // deeppink
+0x90a584d7, 0x644ca940, 0x0000bfff, // deepskyblue
+0x912d3c83, 0x64000000, 0x00696969, // dimgray
+0x91e43965, 0x09952800, 0x001e90ff, // dodgerblue
+0x993228a5, 0x246b0000, 0x00b22222, // firebrick
+0x998f9059, 0x5d09a140, 0x00fffaf0, // floralwhite
+0x99f22ce9, 0x1e452b80, 0x00228b22, // forestgreen
+0x9aa344d3, 0x04000000, 0x00ff00ff, // fuchsia
+0x9c2974c5, 0x3e4f0000, 0x00dcdcdc, // gainsboro
+0x9d0f9d2f, 0x21342800, 0x00f8f8ff, // ghostwhite
+0x9dec2000, 0x00ffd700, // gold
+0x9dec215d, 0x49e40000, 0x00daa520, // goldenrod
+0x9e41c800, 0x00808080, // gray
+0x9e452b80, 0x00008000, // green
+0x9e452bb3, 0x158c7dc0, 0x00adff2f, // greenyellow
+0xa1ee2e49, 0x16e00000, 0x00f0fff0, // honeydew
+0xa1f4825d, 0x2c000000, 0x00ff69b4, // hotpink
+0xa5c4485d, 0x48a40000, 0x00cd5c5c, // indianred
+0xa5c449de, 0x004b0082, // indigo
+0xa6cf9640, 0x00fffff0, // ivory
+0xad015a40, 0x00f0e68c, // khaki
+0xb0362b89, 0x16400000, 0x00e6e6fa, // lavender
+0xb0362b89, 0x16426567, 0x20000000, 0x00fff0f5, // lavenderblush
+0xb03771e5, 0x14ae0000, 0x007cfc00, // lawngreen
+0xb0ad7b87, 0x212633dc, 0x00fffacd, // lemonchiffon
+0xb1274505, 0x32a50000, 0x00add8e6, // lightblue
+0xb1274507, 0x3e416000, 0x00f08080, // lightcoral
+0xb1274507, 0x642e0000, 0x00e0ffff, // lightcyan
+0xb127450f, 0x3d842ba5, 0x3c992b19, 0x3ee00000, 0x00fafad2, // lightgoldenrodyellow
+0xb127450f, 0x48a57000, 0x0090ee90, // lightgreen
+0xb127450f, 0x48b90000, 0x00d3d3d3, // lightgrey
+0xb1274521, 0x25cb0000, 0x00ffb6c1, // lightpink
+0xb1274527, 0x058d7b80, 0x00ffa07a, // lightsalmon
+0xb1274527, 0x1427914b, 0x38000000, 0x0020b2aa, // lightseagreen
+0xb1274527, 0x2f22654a, 0x0087cefa, // lightskyblue
+0xb1274527, 0x303429e5, 0x07200000, 0x00778899, // lightslategray
+0xb1274527, 0x50a56099, 0x54a00000, 0x00b0c4de, // lightsteelblue
+0xb1274533, 0x158c7dc0, 0x00ffffe0, // lightyellow
+0xb12d2800, 0x0000ff00, // lime
+0xb12d29e5, 0x14ae0000, 0x0032cd32, // limegreen
+0xb12e2b80, 0x00faf0e6, // linen
+0xb4272ba9, 0x04000000, 0x00ff00ff, // magenta
+0xb4327bdc, 0x00800000, // maroon
+0xb4a44d5b, 0x06350b43, 0x492e2800, 0x0066cdaa, // mediumaquamarine
+0xb4a44d5b, 0x09952800, 0x000000cd, // mediumblue
+0xb4a44d5b, 0x3e434248, 0x00ba55d3, // mediumorchid
+0xb4a44d5b, 0x42b2830a, 0x009370db, // mediumpurple
+0xb4a44d5b, 0x4ca13c8b, 0x15c00000, 0x003cb371, // mediumseagreen
+0xb4a44d5b, 0x4d81a145, 0x32a50000, 0x007b68ee, // mediumslateblue
+0xb4a44d5b, 0x4e124b8f, 0x1e452b80, 0x0000fa9a, // mediumspringgreen
+0xb4a44d5b, 0x52b28d5f, 0x26650000, 0x0048d1cc, // mediumturquoise
+0xb4a44d5b, 0x592f6169, 0x48a40000, 0x00c71585, // mediumvioletred
+0xb524724f, 0x2282654a, 0x00191970, // midnightblue
+0xb52ea0e5, 0x142d0000, 0x00f5fffa, // mintcream
+0xb533a665, 0x3e650000, 0x00ffe4e1, // mistyrose
+0xb5e31867, 0x25c00000, 0x00ffe4b5, // moccasin
+0xb8360a9f, 0x5d09a140, 0x00ffdead, // navajowhite
+0xb836c800, 0x00000080, // navy
+0xbd846047, 0x14000000, 0x00fdf5e6, // oldlace
+0xbd89b140, 0x00808000, // olive
+0xbd89b149, 0x48220000, 0x006b8e23, // olivedrab
+0xbe4171ca, 0x00ffa500, // orange
+0xbe4171cb, 0x48a40000, 0x00ff4500, // orangered
+0xbe434248, 0x00da70d6, // orchid
+0xc02c29df, 0x3085749f, 0x10000000, 0x00eee8aa, // palegoldenrod
+0xc02c29e5, 0x14ae0000, 0x0098fb98, // palegreen
+0xc02c2d2b, 0x4a357a67, 0x14000000, 0x00afeeee, // paleturquoise
+0xc02c2d93, 0x3d85a48b, 0x10000000, 0x00db7093, // palevioletred
+0xc0300e43, 0x5d098000, 0x00ffefd5, // papayawhip
+0xc0a11a21, 0x54c60000, 0x00ffdab9, // peachpuff
+0xc0b2a800, 0x00cd853f, // peru
+0xc12e5800, 0x00ffc0cb, // pink
+0xc1956800, 0x00dda0dd, // plum
+0xc1f72165, 0x09952800, 0x00b0e0e6, // powderblue
+0xc2b2830a, 0x00800080, // purple
+0xc8a40000, 0x00ff0000, // red
+0xc9f3c8a5, 0x3eee0000, 0x00bc8f8f, // rosybrown
+0xc9f90b05, 0x32a50000, 0x004169e1, // royalblue
+0xcc24230b, 0x0a4fbb80, 0x008b4513, // saddlebrown
+0xcc2c6bdc, 0x00fa8072, // salmon
+0xcc2e2645, 0x49f77000, 0x00f4a460, // sandybrown
+0xcca13c8b, 0x15c00000, 0x002e8b57, // seagreen
+0xcca19a0b, 0x31800000, 0x00fff5ee, // seashell
+0xcd257382, 0x00a0522d, // sienna
+0xcd2cb164, 0x00c0c0c0, // silver
+0xcd79132b, 0x14000000, 0x0087ceeb, // skyblue
+0xcd81a145, 0x32a50000, 0x006a5acd, // slateblue
+0xcd81a14f, 0x48390000, 0x00708090, // slategray
+0xcdcfb800, 0x00fffafa, // snow
+0xce124b8f, 0x1e452b80, 0x0000ff7f, // springgreen
+0xce852b05, 0x32a50000, 0x004682b4, // steelblue
+0xd02e0000, 0x00d2b48c, // tan
+0xd0a16000, 0x00008080, // teal
+0xd1099d19, 0x14000000, 0x00d8bfd8, // thistle
+0xd1ed0d1e, 0x00ff6347, // tomato
+0xd2b28d5f, 0x26650000, 0x0040e0d0, // turquoise
+0xd92f6168, 0x00ee82ee, // violet
+0xdd050d00, 0x00f5deb3, // wheat
+0xdd09a140, 0x00ffffff, // white
+0xdd09a167, 0x35eb2800, 0x00f5f5f5, // whitesmoke
+0xe4ac63ee, 0x00ffff00, // yellow
+0xe4ac63ef, 0x1e452b80, 0x009acd32 // yellowgreen
+}; // original = 2505 : replacement = 1616
+
+
+const char* SkParse::FindNamedColor(const char* name, size_t len, SkColor* color) {
+    const char* namePtr = name;
+    unsigned int sixMatches[4];
+    unsigned int* sixMatchPtr = sixMatches;
+    bool first = true;
+    bool last = false;
+    char ch;
+    do {
+        unsigned int sixMatch = 0;
+        for (int chIndex = 0; chIndex < 6; chIndex++) {
+            sixMatch <<= 5;
+            ch = *namePtr  | 0x20;
+            if (ch < 'a' || ch > 'z')
+                ch = 0;
+            else {
+                ch = ch - 'a' + 1;
+                namePtr++;
+            }
+            sixMatch |= ch ;  // turn 'A' (0x41) into 'a' (0x61);
+        }
+        sixMatch <<= 1;
+        sixMatch |= 1;
+        if (first) {
+            sixMatch |= 0x80000000;
+            first = false;
+        }
+        ch = *namePtr | 0x20;
+        last = ch < 'a' || ch > 'z';
+        if (last)
+            sixMatch &= ~1;
+        len -= 6;
+        *sixMatchPtr++ = sixMatch;
+    } while (last == false && len > 0);
+    const int colorNameSize = sizeof(gColorNames) / sizeof(unsigned int);
+    int lo = 0;
+    int hi = colorNameSize - 3; // back off to beginning of yellowgreen
+    while (lo <= hi) {
+        int mid = (hi + lo) >> 1;
+        while ((int) gColorNames[mid] >= 0)
+            --mid;
+        sixMatchPtr = sixMatches;
+        while (gColorNames[mid] == *sixMatchPtr) {
+            ++mid;
+            if ((*sixMatchPtr & 1) == 0) { // last
+                *color = gColorNames[mid] | 0xFF000000;
+                return namePtr;
+            }
+            ++sixMatchPtr;
+        }
+        int sixMask = *sixMatchPtr & ~0x80000000;
+        int midMask = gColorNames[mid] & ~0x80000000;
+        if (sixMask > midMask) {
+            lo = mid + 2;   // skip color
+            while ((int) gColorNames[lo] >= 0)
+                ++lo;
+        } else if (hi == mid)
+            return NULL;
+        else
+            hi = mid;
+    }
+    return NULL;
+}
+
+// !!! move to char utilities
+//static int count_separators(const char* str, const char* sep) {
+//  char c;
+//  int separators = 0;
+//  while ((c = *str++) != '\0') {
+//      if (strchr(sep, c) == NULL)
+//          continue;
+//      do {
+//          if ((c = *str++) == '\0')
+//              goto goHome;
+//      } while (strchr(sep, c) != NULL);
+//      separators++;
+//  }
+//goHome:
+//  return separators;
+//}
+
+static inline unsigned nib2byte(unsigned n)
+{
+    SkASSERT((n & ~0xF) == 0);
+    return (n << 4) | n;
+}
+
+const char* SkParse::FindColor(const char* value, SkColor* colorPtr) {
+    unsigned int oldAlpha = SkColorGetA(*colorPtr);
+    if (value[0] == '#') {
+        uint32_t    hex;
+        const char* end = SkParse::FindHex(value + 1, &hex);
+//      SkASSERT(end);
+        if (end == NULL)
+            return end;
+        size_t len = end - value - 1;
+        if (len == 3 || len == 4) {
+            unsigned a = len == 4 ? nib2byte(hex >> 12) : oldAlpha;
+            unsigned r = nib2byte((hex >> 8) & 0xF);
+            unsigned g = nib2byte((hex >> 4) & 0xF);
+            unsigned b = nib2byte(hex & 0xF);
+            *colorPtr = SkColorSetARGB(a, r, g, b);
+            return end;
+        } else if (len == 6 || len == 8) {
+            if (len == 6)
+                hex |= oldAlpha << 24;
+            *colorPtr = hex;
+            return end;
+        } else {
+//          SkASSERT(0);
+            return NULL;
+        }
+//  } else if (strchr(value, ',')) {
+//      SkScalar array[4];
+//      int count = count_separators(value, ",") + 1; // !!! count commas, add 1
+//      SkASSERT(count == 3 || count == 4);
+//      array[0] = SK_Scalar1 * 255;
+//      const char* end = SkParse::FindScalars(value, &array[4 - count], count);
+//      if (end == NULL)
+//          return NULL;
+        // !!! range check for errors?
+//      *colorPtr = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]),
+//          SkScalarRound(array[2]), SkScalarRound(array[3]));
+//      return end;
+    } else
+        return FindNamedColor(value, strlen(value), colorPtr);
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkParse::TestColor() {
+    if (false)
+        CreateTable();  // regenerates data table in the output window
+    SkColor result;
+    int index;
+    for (index = 0; index < colorNamesSize; index++) {
+        result = SK_ColorBLACK;
+        SkNameRGB nameRGB = colorNames[index];
+        SkASSERT(FindColor(nameRGB.name, &result) != NULL);
+        SkASSERT(result == (SkColor) (nameRGB.rgb | 0xFF000000));
+    }
+    for (index = 0; index < colorNamesSize; index++) {
+        result = SK_ColorBLACK;
+        SkNameRGB nameRGB = colorNames[index];
+        char bad[24];
+        size_t len = strlen(nameRGB.name);
+        memcpy(bad, nameRGB.name, len);
+        bad[len - 1] -= 1;
+        SkASSERT(FindColor(bad, &result) == false);
+        bad[len - 1] += 2;
+        SkASSERT(FindColor(bad, &result) == false);
+    }
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("lightGrey", &result));
+    SkASSERT(result == 0xffd3d3d3);
+//  SkASSERT(FindColor("12,34,56,78", &result));
+//  SkASSERT(result == ((12 << 24) | (34 << 16) | (56 << 8) | (78 << 0)));
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("#ABCdef", &result));
+    SkASSERT(result == 0XFFABCdef);
+    SkASSERT(FindColor("#12ABCdef", &result));
+    SkASSERT(result == 0X12ABCdef);
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("#123", &result));
+    SkASSERT(result == 0Xff112233);
+    SkASSERT(FindColor("#abcd", &result));
+    SkASSERT(result == 0Xaabbccdd);
+    result = SK_ColorBLACK;
+//  SkASSERT(FindColor("71,162,253", &result));
+//  SkASSERT(result == ((0xFF << 24) | (71 << 16) | (162 << 8) | (253 << 0)));
+}
+#endif
+
diff --git a/legacy/src/utils/SkParsePath.cpp b/legacy/src/utils/SkParsePath.cpp
new file mode 100644
index 0000000..6030493
--- /dev/null
+++ b/legacy/src/utils/SkParsePath.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 "SkParse.h"
+#include "SkParsePath.h"
+
+static inline bool is_between(int c, int min, int max) {
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c) {
+    return is_between(c, 1, 32);
+}
+
+static inline bool is_digit(int c) {
+    return is_between(c, '0', '9');
+}
+
+static inline bool is_sep(int c) {
+    return is_ws(c) || c == ',';
+}
+
+static inline bool is_lower(int c) {
+    return is_between(c, 'a', 'z');
+}
+
+static inline int to_upper(int c) {
+    return c - 'a' + 'A';
+}
+
+static const char* skip_ws(const char str[]) {
+    SkASSERT(str);
+    while (is_ws(*str))
+        str++;
+    return str;
+}
+
+static const char* skip_sep(const char str[]) {
+    SkASSERT(str);
+    while (is_sep(*str))
+        str++;
+    return str;
+}
+
+static const char* find_points(const char str[], SkPoint value[], int count,
+                               bool isRelative, SkPoint* relative) {
+    str = SkParse::FindScalars(str, &value[0].fX, count * 2);
+    if (isRelative) {
+        for (int index = 0; index < count; index++) {
+            value[index].fX += relative->fX;
+            value[index].fY += relative->fY;
+        }
+    }
+    return str;
+}
+
+static const char* find_scalar(const char str[], SkScalar* value, 
+                               bool isRelative, SkScalar relative) {
+    str = SkParse::FindScalar(str, value);
+    if (isRelative) {
+        *value += relative;
+    }
+    return str;
+}
+
+bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
+    SkPath path;
+    SkPoint f = {0, 0};
+    SkPoint c = {0, 0};
+    SkPoint lastc = {0, 0};
+    SkPoint points[3];
+    char op = '\0';
+    char previousOp = '\0';
+    bool relative = false;
+    for (;;) {
+        data = skip_ws(data);
+        if (data[0] == '\0') {
+            break;
+        }
+        char ch = data[0];
+        if (is_digit(ch) || ch == '-' || ch == '+') {
+            if (op == '\0') {
+                return false;
+            }
+        } else {
+            op = ch;
+            relative = false;
+            if (is_lower(op)) {
+                op = (char) to_upper(op);
+                relative = true;
+            }
+            data++;
+            data = skip_sep(data);
+        }
+        switch (op) {
+            case 'M':
+                data = find_points(data, points, 1, relative, &c);
+                path.moveTo(points[0]);
+                op = 'L';
+                c = points[0];
+                break;
+            case 'L': 
+                data = find_points(data, points, 1, relative, &c);
+                path.lineTo(points[0]);
+                c = points[0];
+                break;
+            case 'H': {
+                SkScalar x;
+                data = find_scalar(data, &x, relative, c.fX);
+                path.lineTo(x, c.fY);
+                c.fX = x;
+            } break;
+            case 'V': {
+                SkScalar y;
+                data = find_scalar(data, &y, relative, c.fY);
+                path.lineTo(c.fX, y);
+                c.fY = y;
+            } break;
+            case 'C': 
+                data = find_points(data, points, 3, relative, &c);
+                goto cubicCommon;
+            case 'S': 
+                data = find_points(data, &points[1], 2, relative, &c);
+                points[0] = c;
+                if (previousOp == 'C' || previousOp == 'S') {
+                    points[0].fX -= lastc.fX - c.fX;
+                    points[0].fY -= lastc.fY - c.fY;
+                }
+            cubicCommon:
+                path.cubicTo(points[0], points[1], points[2]);
+                lastc = points[1];
+                c = points[2];
+                break;
+            case 'Q':  // Quadratic Bezier Curve
+                data = find_points(data, points, 2, relative, &c);
+                goto quadraticCommon;
+            case 'T':
+                data = find_points(data, &points[1], 1, relative, &c);
+                points[0] = points[1];
+                if (previousOp == 'Q' || previousOp == 'T') {
+                    points[0].fX = c.fX * 2 - lastc.fX;
+                    points[0].fY = c.fY * 2 - lastc.fY;
+                }
+            quadraticCommon:
+                path.quadTo(points[0], points[1]);
+                lastc = points[0];
+                c = points[1];
+                break;
+            case 'Z':
+                path.close();
+#if 0   // !!! still a bug?
+                if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
+                    c.fX -= SkScalar.Epsilon;   // !!! enough?
+                    fPath.moveTo(c);
+                    fPath.lineTo(f);
+                    fPath.close();
+                }
+#endif
+                c = f;
+                op = '\0';
+                break;
+            case '~': {
+                SkPoint args[2];
+                data = find_points(data, args, 2, false, NULL);
+                path.moveTo(args[0].fX, args[0].fY);
+                path.lineTo(args[1].fX, args[1].fY);
+            } break;
+            default:
+                return false;
+        }
+        if (previousOp == 0) {
+            f = c;
+        }
+        previousOp = op;
+    }
+    // we're good, go ahead and swap in the result
+    result->swap(path);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkString.h"
+#include "SkStream.h"
+
+static void write_scalar(SkWStream* stream, SkScalar value) {
+#ifdef SK_SCALAR_IS_FLOAT
+    char buffer[64];
+#ifdef SK_BUILD_FOR_WIN32
+	int len = _snprintf(buffer, sizeof(buffer), "%g", value);
+#else
+    int len = snprintf(buffer, sizeof(buffer), "%g", value);
+#endif
+    char* stop = buffer + len;
+#else
+    char    buffer[SkStrAppendScalar_MaxSize];
+    char*   stop = SkStrAppendScalar(buffer, value);
+#endif
+    stream->write(buffer, stop - buffer);
+}
+
+static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
+                           int count) {
+    stream->write(&verb, 1);
+    write_scalar(stream, data[0]);
+    for (int i = 1; i < count; i++) {
+        stream->write(" ", 1);
+        write_scalar(stream, data[i]);
+    }
+}
+
+void SkParsePath::ToSVGString(const SkPath& path, SkString* str) {
+    SkDynamicMemoryWStream  stream;
+
+    SkPath::Iter    iter(path, false);
+    SkPoint         pts[4];
+
+    for (;;) {
+        switch (iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                append_scalars(&stream, 'M', &pts[0].fX, 2);
+                break;
+            case SkPath::kLine_Verb:
+                append_scalars(&stream, 'L', &pts[1].fX, 2);
+                break;
+            case SkPath::kQuad_Verb:
+                append_scalars(&stream, 'Q', &pts[1].fX, 4);
+                break;
+            case SkPath::kCubic_Verb:
+                append_scalars(&stream, 'C', &pts[1].fX, 6);
+                break;
+            case SkPath::kClose_Verb:
+                stream.write("Z", 1);
+                break;
+            case SkPath::kDone_Verb:
+                str->resize(stream.getOffset());
+                stream.copyTo(str->writable_str());
+            return;
+        }
+    }
+}
+
diff --git a/legacy/src/utils/SkProxyCanvas.cpp b/legacy/src/utils/SkProxyCanvas.cpp
new file mode 100644
index 0000000..bc21d52
--- /dev/null
+++ b/legacy/src/utils/SkProxyCanvas.cpp
@@ -0,0 +1,156 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkProxyCanvas.h"
+
+SkProxyCanvas::SkProxyCanvas(SkCanvas* proxy) : fProxy(proxy) {
+    SkSafeRef(fProxy);
+}
+
+SkProxyCanvas::~SkProxyCanvas() {
+    SkSafeUnref(fProxy);
+}
+
+void SkProxyCanvas::setProxy(SkCanvas* proxy) {
+    SkRefCnt_SafeAssign(fProxy, proxy);
+}
+
+///////////////////////////////// Overrides ///////////
+
+int SkProxyCanvas::save(SaveFlags flags) {
+    return fProxy->save(flags);
+}
+
+int SkProxyCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                             SaveFlags flags) {
+    return fProxy->saveLayer(bounds, paint, flags);
+}
+
+void SkProxyCanvas::restore() {
+    fProxy->restore();
+}
+
+bool SkProxyCanvas::translate(SkScalar dx, SkScalar dy) {
+    return fProxy->translate(dx, dy);
+}
+
+bool SkProxyCanvas::scale(SkScalar sx, SkScalar sy) {
+    return fProxy->scale(sx, sy);
+}
+
+bool SkProxyCanvas::rotate(SkScalar degrees) {
+    return fProxy->rotate(degrees);
+}
+
+bool SkProxyCanvas::skew(SkScalar sx, SkScalar sy) {
+    return fProxy->skew(sx, sy);
+}
+
+bool SkProxyCanvas::concat(const SkMatrix& matrix) {
+    return fProxy->concat(matrix);
+}
+
+void SkProxyCanvas::setMatrix(const SkMatrix& matrix) {
+    fProxy->setMatrix(matrix);
+}
+
+bool SkProxyCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    return fProxy->clipRect(rect, op, doAA);
+}
+
+bool SkProxyCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    return fProxy->clipPath(path, op, doAA);
+}
+
+bool SkProxyCanvas::clipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+    return fProxy->clipRegion(deviceRgn, op);
+}
+
+void SkProxyCanvas::drawPaint(const SkPaint& paint) {
+    fProxy->drawPaint(paint);
+}
+
+void SkProxyCanvas::drawPoints(PointMode mode, size_t count,
+                               const SkPoint pts[], const SkPaint& paint) {
+    fProxy->drawPoints(mode, count, pts, paint);
+}
+
+void SkProxyCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    fProxy->drawRect(rect, paint);
+}
+
+void SkProxyCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    fProxy->drawPath(path, paint);
+}
+
+void SkProxyCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                               const SkPaint* paint) {
+    fProxy->drawBitmap(bitmap, x, y, paint);
+}
+
+void SkProxyCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                                   const SkRect& dst, const SkPaint* paint) {
+    fProxy->drawBitmapRect(bitmap, src, dst, paint);
+}
+
+void SkProxyCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                     const SkPaint* paint) {
+    fProxy->drawBitmapMatrix(bitmap, m, paint);
+}
+
+void SkProxyCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                               const SkPaint* paint) {
+    fProxy->drawSprite(bitmap, x, y, paint);
+}
+
+void SkProxyCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+                             SkScalar y, const SkPaint& paint) {
+    fProxy->drawText(text, byteLength, x, y, paint);
+}
+
+void SkProxyCanvas::drawPosText(const void* text, size_t byteLength,
+                                const SkPoint pos[], const SkPaint& paint) {
+    fProxy->drawPosText(text, byteLength, pos, paint);
+}
+
+void SkProxyCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                 const SkScalar xpos[], SkScalar constY,
+                                 const SkPaint& paint) {
+    fProxy->drawPosTextH(text, byteLength, xpos, constY, paint);
+}
+
+void SkProxyCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                                   const SkPath& path, const SkMatrix* matrix,
+                                   const SkPaint& paint) {
+    fProxy->drawTextOnPath(text, byteLength, path, matrix, paint);
+}
+
+void SkProxyCanvas::drawPicture(SkPicture& picture) {
+    fProxy->drawPicture(picture);
+}
+
+void SkProxyCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                                 const SkPoint vertices[], const SkPoint texs[],
+                                 const SkColor colors[], SkXfermode* xmode,
+                                 const uint16_t indices[], int indexCount,
+                                 const SkPaint& paint) {
+    fProxy->drawVertices(vmode, vertexCount, vertices, texs, colors,
+                                     xmode, indices, indexCount, paint);
+}
+
+void SkProxyCanvas::drawData(const void* data, size_t length) {
+    fProxy->drawData(data, length);
+}
+
+SkBounder* SkProxyCanvas::setBounder(SkBounder* bounder) {
+    return fProxy->setBounder(bounder);
+}
+
+SkDrawFilter* SkProxyCanvas::setDrawFilter(SkDrawFilter* filter) {
+    return fProxy->setDrawFilter(filter);
+}
+
diff --git a/src/utils/SkSfntUtils.cpp b/legacy/src/utils/SkSfntUtils.cpp
similarity index 100%
rename from src/utils/SkSfntUtils.cpp
rename to legacy/src/utils/SkSfntUtils.cpp
diff --git a/legacy/src/utils/SkUnitMappers.cpp b/legacy/src/utils/SkUnitMappers.cpp
new file mode 100644
index 0000000..583d091
--- /dev/null
+++ b/legacy/src/utils/SkUnitMappers.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 "SkUnitMappers.h"
+
+SkDiscreteMapper::SkDiscreteMapper(int segments) {
+    if (segments < 2) {
+        fSegments = 0;
+        fScale = 0;
+    } else {
+        if (segments > 0xFFFF) {
+            segments = 0xFFFF;
+        }
+        fSegments = segments;
+        fScale = SK_Fract1 / (segments - 1);
+    }
+}
+
+uint16_t SkDiscreteMapper::mapUnit16(uint16_t input) {
+    SkFixed x = input * fSegments >> 16;
+    x = x * fScale >> 14;
+    x += x << 15 >> 31; // map 0x10000 to 0xFFFF
+    return SkToU16(x);
+}
+
+SkDiscreteMapper::SkDiscreteMapper(SkFlattenableReadBuffer& rb)
+        : SkUnitMapper(rb) {
+    fSegments = rb.readU32();
+    fScale = rb.readU32();
+}
+
+SkFlattenable::Factory SkDiscreteMapper::getFactory() {
+    return Create;
+}
+
+SkFlattenable* SkDiscreteMapper::Create(SkFlattenableReadBuffer& rb) {
+    return SkNEW_ARGS(SkDiscreteMapper, (rb));
+}
+
+void SkDiscreteMapper::flatten(SkFlattenableWriteBuffer& wb) {
+    this->INHERITED::flatten(wb);
+
+    wb.write32(fSegments);
+    wb.write32(fScale);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint16_t SkCosineMapper::mapUnit16(uint16_t input)
+{
+    /*  we want to call cosine(input * pi/2) treating input as [0...1)
+        however, the straight multitply would overflow 32bits since input is
+        16bits and pi/2 is 17bits, so we shift down our pi const before we mul
+    */
+    SkFixed rads = (unsigned)(input * (SK_FixedPI >> 2)) >> 15;
+    SkFixed x = SkFixedCos(rads);
+    x += x << 15 >> 31; // map 0x10000 to 0xFFFF
+    return SkToU16(x);
+}
+
+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/unix/SkOSWindow_Unix.cpp b/legacy/src/utils/unix/SkOSWindow_Unix.cpp
similarity index 100%
rename from src/utils/unix/SkOSWindow_Unix.cpp
rename to legacy/src/utils/unix/SkOSWindow_Unix.cpp
diff --git a/src/utils/unix/keysym2ucs.c b/legacy/src/utils/unix/keysym2ucs.c
similarity index 100%
rename from src/utils/unix/keysym2ucs.c
rename to legacy/src/utils/unix/keysym2ucs.c
diff --git a/legacy/src/views/SkBGViewArtist.cpp b/legacy/src/views/SkBGViewArtist.cpp
new file mode 100644
index 0000000..d9a45b8
--- /dev/null
+++ b/legacy/src/views/SkBGViewArtist.cpp
@@ -0,0 +1,31 @@
+
+/*
+ * 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 "SkBGViewArtist.h"
+#include "SkCanvas.h"
+#include "SkParsePaint.h"
+
+SkBGViewArtist::SkBGViewArtist(SkColor c)
+{
+	fPaint.setColor(c);
+}
+
+SkBGViewArtist::~SkBGViewArtist()
+{
+}
+
+void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas)
+{
+	// 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);
+}
+
diff --git a/src/views/SkBorderView.cpp b/legacy/src/views/SkBorderView.cpp
similarity index 100%
rename from src/views/SkBorderView.cpp
rename to legacy/src/views/SkBorderView.cpp
diff --git a/legacy/src/views/SkEvent.cpp b/legacy/src/views/SkEvent.cpp
new file mode 100644
index 0000000..0149215
--- /dev/null
+++ b/legacy/src/views/SkEvent.cpp
@@ -0,0 +1,509 @@
+
+/*
+ * 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 "SkEvent.h"
+
+void SkEvent::initialize(const char* type, size_t typeLen,
+                         SkEventSinkID targetID) {
+    fType = NULL;
+    setType(type, typeLen);
+    f32 = 0;
+    fTargetID = targetID;
+    fTargetProc = NULL;
+#ifdef SK_DEBUG
+    fTime = 0;
+    fNextEvent = NULL;
+#endif
+}
+
+SkEvent::SkEvent()
+{
+    initialize("", 0, 0);
+}
+
+SkEvent::SkEvent(const SkEvent& src)
+{
+    *this = src;
+    if (((size_t) fType & 1) == 0)
+        setType(src.fType);
+}
+
+SkEvent::SkEvent(const SkString& type, SkEventSinkID targetID)
+{
+    initialize(type.c_str(), type.size(), targetID);
+}
+
+SkEvent::SkEvent(const char type[], SkEventSinkID targetID)
+{
+    SkASSERT(type);
+    initialize(type, strlen(type), targetID);
+}
+
+SkEvent::~SkEvent()
+{
+    if (((size_t) fType & 1) == 0)
+        sk_free((void*) fType);
+}
+
+static size_t makeCharArray(char* buffer, size_t compact)
+{
+    size_t bits = (size_t) compact >> 1;
+    memcpy(buffer, &bits, sizeof(compact));
+    buffer[sizeof(compact)] = 0;
+    return strlen(buffer);
+}
+
+void SkEvent::getType(SkString* str) const 
+{ 
+    if (str) 
+    {
+        if ((size_t) fType & 1) // not a pointer
+        {
+            char chars[sizeof(size_t) + 1];
+            size_t len = makeCharArray(chars, (size_t) fType);
+            str->set(chars, len);
+        }
+        else
+            str->set(fType);
+    }
+}
+
+bool SkEvent::isType(const SkString& str) const 
+{
+    return this->isType(str.c_str(), str.size()); 
+}
+
+bool SkEvent::isType(const char type[], size_t typeLen) const 
+{ 
+    if (typeLen == 0)
+        typeLen = strlen(type);
+    if ((size_t) fType & 1) {   // not a pointer
+        char chars[sizeof(size_t) + 1];
+        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; 
+}
+
+void SkEvent::setType(const char type[], size_t typeLen)
+{
+    if (typeLen == 0)
+        typeLen = strlen(type);
+    if (typeLen <= sizeof(fType)) {
+        size_t slot = 0;
+        memcpy(&slot, type, typeLen);
+        if (slot << 1 >> 1 != slot)
+            goto useCharStar;
+        slot <<= 1;
+        slot |= 1;
+        fType = (char*) slot;
+    } else {
+useCharStar:
+        fType = (char*) sk_malloc_throw(typeLen + 1);
+        SkASSERT(((size_t) fType & 1) == 0);
+        memcpy(fType, type, typeLen);
+        fType[typeLen] = 0;
+    }
+}
+
+void SkEvent::setType(const SkString& type)
+{
+    setType(type.c_str());
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#include "SkParse.h"
+
+void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+    const char* name = dom.findAttr(node, "type");
+    if (name)
+        this->setType(name);
+
+    const char* value;
+    if ((value = dom.findAttr(node, "fast32")) != NULL)
+    {
+        int32_t n;
+        if (SkParse::FindS32(value, &n))
+            this->setFast32(n);
+    }
+
+    for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node))
+    {
+        if (strcmp(dom.getName(node), "data"))
+        {
+            SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));)
+            continue;
+        }
+
+        name = dom.findAttr(node, "name");
+        if (name == NULL)
+        {
+            SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in <data> subelement\n");)
+            continue;
+        }
+
+        if ((value = dom.findAttr(node, "s32")) != NULL)
+        {
+            int32_t n;
+            if (SkParse::FindS32(value, &n))
+                this->setS32(name, n);
+        }
+        else if ((value = dom.findAttr(node, "scalar")) != NULL)
+        {
+            SkScalar x;
+            if (SkParse::FindScalar(value, &x))
+                this->setScalar(name, x);
+        }
+        else if ((value = dom.findAttr(node, "string")) != NULL)
+            this->setString(name, value);
+#ifdef SK_DEBUG
+        else
+        {
+            SkDebugf("SkEvent::inflate <data name=\"%s\"> subelement missing required type attribute [S32 | scalar | string]\n", name);
+        }
+#endif
+    }
+}
+
+#ifdef SK_DEBUG
+
+    #ifndef SkScalarToFloat
+        #define SkScalarToFloat(x)  ((x) / 65536.f)
+    #endif
+
+    void SkEvent::dump(const char title[])
+    {
+        if (title)
+            SkDebugf("%s ", title);
+            
+        SkString    etype;
+        this->getType(&etype);
+        SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32());
+
+        const SkMetaData&   md = this->getMetaData();
+        SkMetaData::Iter    iter(md);
+        SkMetaData::Type    mtype;
+        int                 count;
+        const char*         name;
+        
+        while ((name = iter.next(&mtype, &count)) != NULL)
+        {
+            SkASSERT(count > 0);
+
+            SkDebugf(" <%s>=", name);
+            switch (mtype) {
+            case SkMetaData::kS32_Type:     // vector version???
+                {
+                    int32_t value;
+                    md.findS32(name, &value);
+                    SkDebugf("%d ", value);
+                }
+                break;
+            case SkMetaData::kScalar_Type:
+                {
+                    const SkScalar* values = md.findScalars(name, &count, NULL);
+                    SkDebugf("%f", SkScalarToFloat(values[0]));
+                    for (int i = 1; i < count; i++)
+                        SkDebugf(", %f", SkScalarToFloat(values[i]));
+                    SkDebugf(" ");
+                }
+                break;
+            case SkMetaData::kString_Type:
+                {
+                    const char* value = md.findString(name);
+                    SkASSERT(value);
+                    SkDebugf("<%s> ", value);
+                }
+                break;
+            case SkMetaData::kPtr_Type:     // vector version???
+                {
+                    void*   value;
+                    md.findPtr(name, &value);
+                    SkDebugf("%p ", value);
+                }
+                break;
+            case SkMetaData::kBool_Type:    // vector version???
+                {
+                    bool    value;
+                    md.findBool(name, &value);
+                    SkDebugf("%s ", value ? "true" : "false");
+                }
+                break;
+            default:
+                SkDEBUGFAIL("unknown metadata type returned from iterator");
+                break;
+            }
+        }
+        SkDebugf("\n");
+    }
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+// #define SK_TRACE_EVENTSx
+#endif
+
+#ifdef SK_TRACE_EVENTS
+    static void event_log(const char s[])
+    {
+        SkDEBUGF(("%s\n", s));
+    }
+
+    #define EVENT_LOG(s)        event_log(s)
+    #define EVENT_LOGN(s, n)    do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0)
+#else
+    #define EVENT_LOG(s)
+    #define EVENT_LOGN(s, n)
+#endif
+
+#include "SkThread.h"
+#include "SkTime.h"
+
+class SkEvent_Globals {
+public:
+    SkEvent_Globals() {
+        fEventQHead = NULL;
+        fEventQTail = NULL;
+        fDelayQHead = NULL;
+        SkDEBUGCODE(fEventCounter = 0;)
+    }
+
+    SkMutex     fEventMutex;
+    SkEvent*    fEventQHead, *fEventQTail;
+    SkEvent*    fDelayQHead;
+    SkDEBUGCODE(int fEventCounter;)
+};
+
+static SkEvent_Globals& getGlobals() {
+    // leak this, so we don't incure any shutdown perf hit
+    static SkEvent_Globals* gGlobals = new SkEvent_Globals;
+    return *gGlobals;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkEvent::postDelay(SkMSec delay) {
+    if (!fTargetID && !fTargetProc) {
+        delete this;
+        return;
+    }
+    
+    if (delay) {
+        this->postTime(SkTime::GetMSecs() + delay);
+        return;
+    }
+
+    SkEvent_Globals& globals = getGlobals();
+
+    globals.fEventMutex.acquire();
+    bool wasEmpty = SkEvent::Enqueue(this);
+    globals.fEventMutex.release();
+    
+    // call outside of us holding the mutex
+    if (wasEmpty) {
+        SkEvent::SignalNonEmptyQueue();
+    }
+}
+
+void SkEvent::postTime(SkMSec time) {
+    if (!fTargetID && !fTargetProc) {
+        delete this;
+        return;
+    }
+
+    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);
+    }
+}
+
+bool SkEvent::Enqueue(SkEvent* evt) {
+    SkEvent_Globals& globals = getGlobals();
+    //  gEventMutex acquired by caller
+
+    SkASSERT(evt);
+
+    bool wasEmpty = globals.fEventQHead == NULL;
+
+    if (globals.fEventQTail)
+        globals.fEventQTail->fNextEvent = evt;
+    globals.fEventQTail = evt;
+    if (globals.fEventQHead == NULL)
+        globals.fEventQHead = evt;
+    evt->fNextEvent = NULL;
+
+    SkDEBUGCODE(++globals.fEventCounter);
+
+    return wasEmpty;
+}
+
+SkEvent* SkEvent::Dequeue() {
+    SkEvent_Globals& globals = getGlobals();
+    globals.fEventMutex.acquire();
+
+    SkEvent* evt = globals.fEventQHead;
+    if (evt) {
+        SkDEBUGCODE(--globals.fEventCounter);
+
+        globals.fEventQHead = evt->fNextEvent;
+        if (globals.fEventQHead == NULL) {
+            globals.fEventQTail = NULL;
+        }
+    }
+    globals.fEventMutex.release();
+
+    return evt;
+}
+
+bool SkEvent::QHasEvents() {
+    SkEvent_Globals& globals = getGlobals();
+
+    // this is not thread accurate, need a semaphore for that
+    return globals.fEventQHead != NULL;
+}
+
+#ifdef SK_TRACE_EVENTS
+    static int gDelayDepth;
+#endif
+
+SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time) {
+    SkEvent_Globals& globals = getGlobals();
+    //  gEventMutex acquired by caller
+
+    SkEvent* curr = globals.fDelayQHead;
+    SkEvent* prev = NULL;
+
+    while (curr) {
+        if (SkMSec_LT(time, curr->fTime)) {
+            break;
+        }
+        prev = curr;
+        curr = curr->fNextEvent;
+    }
+
+    evt->fTime = time;
+    evt->fNextEvent = curr;
+    if (prev == NULL) {
+        globals.fDelayQHead = evt;
+    } else {
+        prev->fNextEvent = evt;
+    }
+
+    SkMSec delay = globals.fDelayQHead->fTime - SkTime::GetMSecs();
+    if ((int32_t)delay <= 0) {
+        delay = 1;
+    }
+    return delay;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkEventSink.h"
+
+bool SkEvent::ProcessEvent() {
+    SkEvent*                evt = SkEvent::Dequeue();
+    SkAutoTDelete<SkEvent>  autoDelete(evt);
+    bool                    again = false;
+
+    EVENT_LOGN("ProcessEvent", (int32_t)evt);
+
+    if (evt) {
+        (void)SkEventSink::DoEvent(*evt);
+        again = SkEvent::QHasEvents();
+    }
+    return again;
+}
+
+void SkEvent::ServiceQueueTimer()
+{
+    SkEvent_Globals& globals = getGlobals();
+
+    globals.fEventMutex.acquire();
+
+    bool        wasEmpty = false;
+    SkMSec      now = SkTime::GetMSecs();
+    SkEvent*    evt = globals.fDelayQHead;
+
+    while (evt)
+    {
+        if (SkMSec_LT(now, evt->fTime))
+            break;
+
+#ifdef SK_TRACE_EVENTS
+        --gDelayDepth;
+        SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth);
+        const char* idStr = evt->findString("id");
+        if (idStr)
+            SkDebugf(" (%s)", idStr);
+        SkDebugf("\n");
+#endif
+
+        SkEvent* next = evt->fNextEvent;
+        if (SkEvent::Enqueue(evt))
+            wasEmpty = true;
+        evt = next;
+    }
+    globals.fDelayQHead = evt;
+
+    SkMSec time = evt ? evt->fTime - now : 0;
+
+    globals.fEventMutex.release();
+
+    if (wasEmpty)
+        SkEvent::SignalNonEmptyQueue();
+
+    SkEvent::SignalQueueTimer(time);
+}
+
+int SkEvent::CountEventsOnQueue() {
+    SkEvent_Globals& globals = getGlobals();
+    globals.fEventMutex.acquire();
+    
+    int count = 0;
+    const SkEvent* evt = globals.fEventQHead;
+    while (evt) {
+        count += 1;
+        evt = evt->fNextEvent;
+    }
+    globals.fEventMutex.release();
+
+    return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkEvent::Init() {}
+
+void SkEvent::Term() {
+    SkEvent_Globals& globals = getGlobals();
+
+    SkEvent* evt = globals.fEventQHead;
+    while (evt) {
+        SkEvent* next = evt->fNextEvent;
+        delete evt;
+        evt = next;
+    }
+
+    evt = globals.fDelayQHead;
+    while (evt) {
+        SkEvent* next = evt->fNextEvent;
+        delete evt;
+        evt = next;
+    }
+}
+
diff --git a/legacy/src/views/SkEventSink.cpp b/legacy/src/views/SkEventSink.cpp
new file mode 100644
index 0000000..20d8cdf
--- /dev/null
+++ b/legacy/src/views/SkEventSink.cpp
@@ -0,0 +1,303 @@
+
+/*
+ * 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 "SkEventSink.h"
+#include "SkTagList.h"
+#include "SkThread.h"
+
+#include "SkThread.h"
+#include "SkTime.h"
+
+class SkEventSink_Globals {
+public:
+    SkEventSink_Globals() {
+        fNextSinkID = 0;
+        fSinkHead = NULL;
+    }
+
+    SkMutex         fSinkMutex;
+    SkEventSinkID   fNextSinkID;
+    SkEventSink*    fSinkHead;
+};
+
+static SkEventSink_Globals& getGlobals() {
+    // leak this, so we don't incur any shutdown perf hit
+    static SkEventSink_Globals* gGlobals = new SkEventSink_Globals;
+    return *gGlobals;
+}
+
+SkEventSink::SkEventSink() : fTagHead(NULL) {
+    SkEventSink_Globals& globals = getGlobals();
+
+    globals.fSinkMutex.acquire();
+
+    fID = ++globals.fNextSinkID;
+    fNextSink = globals.fSinkHead;
+    globals.fSinkHead = this;
+
+    globals.fSinkMutex.release();
+}
+
+SkEventSink::~SkEventSink() {
+    SkEventSink_Globals& globals = getGlobals();
+
+    if (fTagHead)
+        SkTagList::DeleteAll(fTagHead);
+
+    globals.fSinkMutex.acquire();
+
+    SkEventSink* sink = globals.fSinkHead;
+    SkEventSink* prev = NULL;
+
+    for (;;) {
+        SkEventSink* next = sink->fNextSink;
+        if (sink == this) {
+            if (prev) {
+                prev->fNextSink = next;
+            } else {
+                globals.fSinkHead = next;
+            }
+            break;
+        }
+        prev = sink;
+        sink = next;
+    }
+    globals.fSinkMutex.release();
+}
+
+bool SkEventSink::doEvent(const SkEvent& evt) {
+    return this->onEvent(evt);
+}
+
+bool SkEventSink::doQuery(SkEvent* evt) {
+    SkASSERT(evt);
+    return this->onQuery(evt);
+}
+
+bool SkEventSink::onEvent(const SkEvent&) {
+    return false;
+}
+
+bool SkEventSink::onQuery(SkEvent*) {
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTagList* SkEventSink::findTagList(U8CPU tag) const {
+    return fTagHead ? SkTagList::Find(fTagHead, tag) : NULL;
+}
+
+void SkEventSink::addTagList(SkTagList* rec) {
+    SkASSERT(rec);
+    SkASSERT(fTagHead == NULL || SkTagList::Find(fTagHead, rec->fTag) == NULL);
+
+    rec->fNext = fTagHead;
+    fTagHead = rec;
+}
+
+void SkEventSink::removeTagList(U8CPU tag) {
+    if (fTagHead) {
+        SkTagList::DeleteTag(&fTagHead, tag);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkListenersTagList : SkTagList {
+    SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList)
+    {
+        fExtra16 = SkToU16(count);
+        fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID));
+    }
+    virtual ~SkListenersTagList()
+    {
+        sk_free(fIDs);
+    }
+
+    int countListners() const { return fExtra16; }
+
+    int find(SkEventSinkID id) const
+    {
+        const SkEventSinkID* idptr = fIDs;
+        for (int i = fExtra16 - 1; i >= 0; --i)
+            if (idptr[i] == id)
+                return i;
+        return -1;
+    }
+
+    SkEventSinkID*  fIDs;
+};
+
+void SkEventSink::addListenerID(SkEventSinkID id)
+{
+    if (id == 0)
+        return;
+
+    SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+    int                 count = 0;
+
+    if (prev)
+    {
+        if (prev->find(id) >= 0)
+            return;
+        count = prev->countListners();
+    }
+
+    SkListenersTagList* next = SkNEW_ARGS(SkListenersTagList, (count + 1));
+
+    if (prev)
+    {
+        memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID));
+        this->removeTagList(kListeners_SkTagList);
+    }
+    next->fIDs[count] = id;
+    this->addTagList(next);
+}
+
+void SkEventSink::copyListeners(const SkEventSink& sink) 
+{
+    SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList);
+    if (sinkList == NULL)
+        return;
+    SkASSERT(sinkList->countListners() > 0);
+    const SkEventSinkID* iter = sinkList->fIDs;
+    const SkEventSinkID* stop = iter + sinkList->countListners();
+    while (iter < stop)
+        addListenerID(*iter++);
+}
+
+void SkEventSink::removeListenerID(SkEventSinkID id)
+{
+    if (id == 0)
+        return;
+
+    SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+
+    if (list == NULL)
+        return;
+
+    int index = list->find(id);
+    if (index >= 0)
+    {
+        int count = list->countListners();
+        SkASSERT(count > 0);
+        if (count == 1)
+            this->removeTagList(kListeners_SkTagList);
+        else
+        {
+            // overwrite without resize/reallocating our struct (for speed)
+            list->fIDs[index] = list->fIDs[count - 1];
+            list->fExtra16 = SkToU16(count - 1);
+        }
+    }
+}
+
+bool SkEventSink::hasListeners() const
+{
+    return this->findTagList(kListeners_SkTagList) != NULL;
+}
+
+void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay) {
+    SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+    if (list) {
+        SkASSERT(list->countListners() > 0);
+        const SkEventSinkID* iter = list->fIDs;
+        const SkEventSinkID* stop = iter + list->countListners();
+        while (iter < stop) {
+            SkEvent* copy = SkNEW_ARGS(SkEvent, (evt));
+            copy->setTargetID(*iter++)->postDelay(delay);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt) {
+    SkEvent::Proc proc = evt.getTargetProc();
+    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;
+    }
+
+    return kSinkNotFound_EventResult;
+}
+
+SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID)
+{
+    if (sinkID == 0)
+        return 0;
+
+    SkEventSink_Globals&    globals = getGlobals();
+    SkAutoMutexAcquire      ac(globals.fSinkMutex);
+    SkEventSink*            sink = globals.fSinkHead;
+
+    while (sink)
+    {
+        if (sink->getSinkID() == sinkID)
+            return sink;
+        sink = sink->fNextSink;
+    }
+    return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0   // experimental, not tested
+
+#include "SkThread.h"
+#include "SkTDict.h"
+
+#define kMinStringBufferSize    128
+SK_DECLARE_STATIC_MUTEX(gNamedSinkMutex);
+static SkTDict<SkEventSinkID>   gNamedSinkIDs(kMinStringBufferSize);
+
+/** Register a name/id pair with the system. If the name already exists,
+    replace its ID with the new id. This pair will persist until UnregisterNamedSink()
+    is called.
+*/
+void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id)
+{
+    if (id && name && *name)
+    {
+        SkAutoMutexAcquire  ac(gNamedSinkMutex);
+        gNamedSinkIDs.set(name, id);
+    }
+}
+
+/** Return the id that matches the specified name (from a previous call to
+    RegisterNamedSinkID(). If no match is found, return 0
+*/
+SkEventSinkID SkEventSink::FindNamedSinkID(const char name[])
+{
+    SkEventSinkID id = 0;
+
+    if (name && *name)
+    {
+        SkAutoMutexAcquire  ac(gNamedSinkMutex);
+        (void)gNamedSinkIDs.find(name, &id);
+    }
+    return id;
+}
+
+/** Remove all name/id pairs from the system. This is call internally
+    on shutdown, to ensure no memory leaks. It should not be called
+    before shutdown.
+*/
+void SkEventSink::RemoveAllNamedSinkIDs()
+{
+    SkAutoMutexAcquire  ac(gNamedSinkMutex);
+    (void)gNamedSinkIDs.reset();
+}
+#endif
diff --git a/src/views/SkImageView.cpp b/legacy/src/views/SkImageView.cpp
similarity index 100%
rename from src/views/SkImageView.cpp
rename to legacy/src/views/SkImageView.cpp
diff --git a/src/views/SkListView.cpp b/legacy/src/views/SkListView.cpp
similarity index 100%
rename from src/views/SkListView.cpp
rename to legacy/src/views/SkListView.cpp
diff --git a/src/views/SkListWidget.cpp b/legacy/src/views/SkListWidget.cpp
similarity index 100%
rename from src/views/SkListWidget.cpp
rename to legacy/src/views/SkListWidget.cpp
diff --git a/legacy/src/views/SkOSMenu.cpp b/legacy/src/views/SkOSMenu.cpp
new file mode 100644
index 0000000..ed37541
--- /dev/null
+++ b/legacy/src/views/SkOSMenu.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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 <stdarg.h>
+#include "SkOSMenu.h"
+#include "SkThread.h"
+
+static int gOSMenuCmd = 7000;
+
+SkOSMenu::SkOSMenu(const char title[]) {
+	fTitle.set(title);
+}
+
+SkOSMenu::~SkOSMenu() {
+    this->reset();
+}
+
+void SkOSMenu::reset() {
+    fItems.deleteAll();
+    fTitle.reset();
+}
+
+const SkOSMenu::Item* SkOSMenu::getItemByID(int itemID) const {
+    for (int i = 0; i < fItems.count(); ++i) {
+        if (itemID == fItems[i]->getID())
+            return fItems[i];
+    }
+    return NULL;
+}
+
+void SkOSMenu::getItems(const SkOSMenu::Item* items[]) const {
+    if (NULL != items) {
+        for (int i = 0; i < fItems.count(); ++i) {
+            items[i] = fItems[i];
+        }
+    }
+}
+
+void SkOSMenu::assignKeyEquivalentToItem(int itemID, SkUnichar key) {
+    for (int i = 0; i < fItems.count(); ++i) {
+        if (itemID == fItems[i]->getID())
+            fItems[i]->setKeyEquivalent(key);
+    }
+}
+
+bool SkOSMenu::handleKeyEquivalent(SkUnichar key) {    
+    int value = 0, size = 0;
+    bool state;
+    SkOSMenu::TriState tristate;
+    for (int i = 0; i < fItems.count(); ++i) {
+        Item* item = fItems[i];
+        if (item->getKeyEquivalent()== key) {
+            SkString list;
+            switch (item->getType()) {
+                case kList_Type:
+                    SkOSMenu::FindListItemCount(*item->getEvent(), &size);
+                    SkOSMenu::FindListIndex(*item->getEvent(), item->getSlotName(), &value);
+                    value = (value + 1) % size;
+                    item->setInt(value);
+                    break;
+                case kSwitch_Type:
+                    SkOSMenu::FindSwitchState(*item->getEvent(), item->getSlotName(), &state);
+                    item->setBool(!state);
+                    break;
+                case kTriState_Type:
+                    SkOSMenu::FindTriState(*item->getEvent(), item->getSlotName(), &tristate);
+                    if (kOnState == tristate)
+                        tristate = kMixedState;
+                    else
+                        tristate = (SkOSMenu::TriState)((int)tristate + 1);
+                    item->setTriState(tristate);
+                    break;
+                case kAction_Type:
+                case kCustom_Type:
+                case kSlider_Type:
+                case kTextField_Type:
+                default:
+                    break;
+            }
+            item->postEvent();
+            return true;
+        }
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkOSMenu::Item::Item(const char label[], SkOSMenu::Type type, 
+                     const char slotName[], SkEvent* evt) {
+    fLabel.set(label);
+    fSlotName.set(slotName);
+    fType = type;
+    fEvent = evt;
+    fKey = 0;
+    fID = sk_atomic_inc(&gOSMenuCmd);
+}
+
+void SkOSMenu::Item::setBool(bool value) const {
+    SkASSERT(SkOSMenu::kSwitch_Type == fType);
+    fEvent->setBool(fSlotName.c_str(), value);
+}
+
+void SkOSMenu::Item::setScalar(SkScalar value) const {
+    SkASSERT(SkOSMenu::kSlider_Type == fType);
+    fEvent->setScalar(fSlotName.c_str(), value);
+}
+
+void SkOSMenu::Item::setInt(int value) const {
+    SkASSERT(SkOSMenu::kList_Type == fType);
+    fEvent->setS32(fSlotName.c_str(), value);
+}
+
+void SkOSMenu::Item::setTriState(TriState value) const {
+    SkASSERT(SkOSMenu::kTriState_Type == fType);
+    fEvent->setS32(fSlotName.c_str(), value);
+}
+
+void SkOSMenu::Item::setString(const char value[]) const {
+    SkASSERT(SkOSMenu::kTextField_Type == fType);
+    fEvent->setString(fSlotName.c_str(), value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static const char* gMenuEventType = "SkOSMenuEventType";
+static const char* gSlider_Min_Scalar = "SkOSMenuSlider_Min";
+static const char* gSlider_Max_Scalar = "SkOSMenuSlider_Max";
+static const char* gDelimiter = "|";
+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[], 
+                         SkEvent* evt) {
+    SkOSMenu::Item* item = new Item(label, type, slotName, evt);
+    fItems.append(1, &item);
+    return item->getID();
+}
+
+int SkOSMenu::appendAction(const char label[], SkEventSinkID target) {
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
+    //Store label in event so it can be used to identify the action later
+    evt->setString(label, label);
+    return appendItem(label, SkOSMenu::kAction_Type, "", evt);
+}
+
+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;
+    if (option) {
+        SkString str(option);
+        va_start(args, option);
+        int count = 1;
+        for (const char* arg = va_arg(args, const char*); arg != NULL; arg = va_arg(args, const char*)) {
+            str += gDelimiter;
+            str += arg;
+            ++count;
+        }
+        va_end(args);
+        evt->setString(gList_Items_Str, str);
+        evt->setS32(gList_ItemCount_S32, count);
+        evt->setS32(slotName, index);
+    }
+    return appendItem(label, SkOSMenu::kList_Type, slotName, evt);
+}
+
+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);
+    evt->setScalar(gSlider_Max_Scalar, max);
+    evt->setScalar(slotName, defaultValue);
+    return appendItem(label, SkOSMenu::kSlider_Type, slotName, evt);
+}
+
+int SkOSMenu::appendSwitch(const char label[], const char slotName[], 
+                           SkEventSinkID target, bool defaultState) {
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
+    evt->setBool(slotName, defaultState);
+    return appendItem(label, SkOSMenu::kSwitch_Type, slotName, evt);
+}
+
+int SkOSMenu::appendTriState(const char label[], const char slotName[],
+                             SkEventSinkID target, SkOSMenu::TriState defaultState) {
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
+    evt->setS32(slotName, defaultState);
+    return appendItem(label, SkOSMenu::kTriState_Type, slotName, evt);
+}
+
+int SkOSMenu::appendTextField(const char label[], const char slotName[], 
+                              SkEventSinkID target, const char placeholder[]) {
+    SkEvent* evt = new SkEvent(gMenuEventType, target);
+    evt->setString(slotName, placeholder);
+    return appendItem(label, SkOSMenu::kTextField_Type, slotName, evt);
+}
+
+bool SkOSMenu::FindListItemCount(const SkEvent& evt, int* count) {
+    return evt.isType(gMenuEventType) && evt.findS32(gList_ItemCount_S32, count);
+}
+
+bool SkOSMenu::FindListItems(const SkEvent& evt, SkString items[]) {
+    if (evt.isType(gMenuEventType) && NULL != items) {
+        const char* text = evt.findString(gList_Items_Str);
+        if (text != NULL) {
+            SkString temp(text);
+            char* token = strtok((char*)temp.c_str(), gDelimiter);
+            int index = 0;
+            while (token != NULL) {
+                items[index].set(token, strlen(token));
+                token = strtok (NULL, gDelimiter);
+                ++index;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkOSMenu::FindSliderMin(const SkEvent& evt, SkScalar* min) {
+    return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Min_Scalar, min);
+}
+
+bool SkOSMenu::FindSliderMax(const SkEvent& evt, SkScalar* max) {
+    return evt.isType(gMenuEventType) && evt.findScalar(gSlider_Max_Scalar, max);
+}
+
+bool SkOSMenu::FindAction(const SkEvent& evt, const char label[]) {
+    return evt.isType(gMenuEventType) && evt.findString(label);
+}
+
+bool SkOSMenu::FindListIndex(const SkEvent& evt, const char slotName[], int* value) {
+    return evt.isType(gMenuEventType) && evt.findS32(slotName, value); 
+}
+
+bool SkOSMenu::FindSliderValue(const SkEvent& evt, const char slotName[], SkScalar* value) {
+    return evt.isType(gMenuEventType) && evt.findScalar(slotName, value);
+}
+
+bool SkOSMenu::FindSwitchState(const SkEvent& evt, const char slotName[], bool* value) {
+    return evt.isType(gMenuEventType) && evt.findBool(slotName, value);
+}
+
+bool SkOSMenu::FindTriState(const SkEvent& evt, const char slotName[], SkOSMenu::TriState* value) {
+    return evt.isType(gMenuEventType) && evt.findS32(slotName, (int*)value);
+}
+
+bool SkOSMenu::FindText(const SkEvent& evt, const char slotName[], SkString* value) {
+    if (evt.isType(gMenuEventType)) {
+        const char* text = evt.findString(slotName);
+        if (!text || !*text)
+            return false;
+        else {
+            value->set(text);
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/legacy/src/views/SkParsePaint.cpp b/legacy/src/views/SkParsePaint.cpp
new file mode 100644
index 0000000..4839439
--- /dev/null
+++ b/legacy/src/views/SkParsePaint.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 "SkParsePaint.h"
+#include "SkTSearch.h"
+#include "SkParse.h"
+#include "SkImageDecoder.h"
+#include "SkGradientShader.h"
+
+static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node)
+{
+	if ((node = dom.getFirstChild(node, "shader")) == NULL)
+		return NULL;
+
+	const char* str;
+
+	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;
+
+			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;
+
+		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;
+
+			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);
+
+	SkScalar x;
+
+	if (dom.findScalar(node, "stroke-width", &x))
+		paint->setStrokeWidth(x);
+	if (dom.findScalar(node, "text-size", &x))
+		paint->setTextSize(x);
+	
+	bool	b;
+
+	SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &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);
+
+	const char* str = dom.findAttr(node, "color");
+	if (str)
+	{
+		SkColor	c = paint->getColor();
+		if (SkParse::FindColor(str, &c))
+			paint->setColor(c);
+	}
+
+	// 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));
+	}
+
+	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/legacy/src/views/SkProgressBarView.cpp
similarity index 100%
rename from src/views/SkProgressBarView.cpp
rename to legacy/src/views/SkProgressBarView.cpp
diff --git a/legacy/src/views/SkProgressView.cpp b/legacy/src/views/SkProgressView.cpp
new file mode 100644
index 0000000..d82b48e
--- /dev/null
+++ b/legacy/src/views/SkProgressView.cpp
@@ -0,0 +1,133 @@
+
+/*
+ * 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 "SkMath.h"
+#include "SkShader.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+
+SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL)
+{
+	fValue = 0;
+	fMax = 0;
+	fInterp = NULL;
+	fDoInterp = false;
+}
+
+SkProgressView::~SkProgressView()
+{
+	delete fInterp;
+	SkSafeUnref(fOnShader);
+	SkSafeUnref(fOffShader);
+}
+
+void SkProgressView::setMax(U16CPU max)
+{
+	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);
+	}
+}
+
+void SkProgressView::onDraw(SkCanvas* canvas)
+{
+	if (fMax == 0)
+		return;
+
+	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);
+	}
+
+
+	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);
+
+	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;
+
+	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);
+
+	const char* s;
+
+	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);
+}
+
diff --git a/src/views/SkScrollBarView.cpp b/legacy/src/views/SkScrollBarView.cpp
similarity index 100%
rename from src/views/SkScrollBarView.cpp
rename to legacy/src/views/SkScrollBarView.cpp
diff --git a/legacy/src/views/SkStackViewLayout.cpp b/legacy/src/views/SkStackViewLayout.cpp
new file mode 100644
index 0000000..bf6f363
--- /dev/null
+++ b/legacy/src/views/SkStackViewLayout.cpp
@@ -0,0 +1,274 @@
+
+/*
+ * 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 "SkStackViewLayout.h"
+
+SkStackViewLayout::SkStackViewLayout()
+{
+	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);
+}
+
+void SkStackViewLayout::getMargin(SkRect* margin) const
+{
+	if (margin)
+		*margin = fMargin;
+}
+
+void SkStackViewLayout::setMargin(const SkRect& margin)
+{
+	fMargin = margin;
+}
+
+void SkStackViewLayout::setSpacer(SkScalar spacer)
+{
+	fSpacer = spacer;
+}
+
+void SkStackViewLayout::setPack(Pack pack)
+{
+	SkASSERT((unsigned)pack < kPackCount);
+	fPack = SkToU8(pack);
+}
+
+void SkStackViewLayout::setAlign(Align align)
+{
+	SkASSERT((unsigned)align < kAlignCount);
+	fAlign = SkToU8(align);
+}
+
+void SkStackViewLayout::setRound(bool r)
+{
+	fRound = SkToU8(r);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
+typedef SkScalar (SkView::*GetSizeProc)() const;
+typedef void (SkView::*SetLocProc)(SkScalar coord);
+typedef void (SkView::*SetSizeProc)(SkScalar coord);
+
+static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
+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
+*/
+static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
+									   uint32_t flexMask, int* flexCount)
+{
+	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;
+}
+
+void SkStackViewLayout::onLayoutChildren(SkView* parent)
+{
+	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;
+
+	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;
+
+		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;
+
+		mainGetSizeP	= &SkView::height;
+		crossGetSizeP	= &SkView::width;
+		mainLocP	= &SkView::setLocY;
+		crossLocP	= &SkView::setLocX;
+
+		mainSetSizeP  = &SkView::setHeight;
+		crossSetSizeP = &SkView::setWidth;
+
+		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);
+
+	if (childCount == 0)
+		return;
+
+	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;
+
+	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);
+
+		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);
+	}
+#else
+	#define assert_no_attr(dom, node, attr)
+#endif
+
+void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	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 (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 ((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");
+        }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+SkFillViewLayout::SkFillViewLayout()
+{
+	fMargin.setEmpty();
+}
+
+void SkFillViewLayout::getMargin(SkRect* r) const
+{
+	if (r)
+		*r = fMargin;
+}
+
+void SkFillViewLayout::setMargin(const SkRect& margin)
+{
+	fMargin = margin;
+}
+
+void SkFillViewLayout::onLayoutChildren(SkView* parent)
+{
+	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);
+	}
+}
+
+void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+	(void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
+}
+
diff --git a/src/views/SkStaticTextView.cpp b/legacy/src/views/SkStaticTextView.cpp
similarity index 100%
rename from src/views/SkStaticTextView.cpp
rename to legacy/src/views/SkStaticTextView.cpp
diff --git a/legacy/src/views/SkTagList.cpp b/legacy/src/views/SkTagList.cpp
new file mode 100644
index 0000000..e1b1662
--- /dev/null
+++ b/legacy/src/views/SkTagList.cpp
@@ -0,0 +1,63 @@
+
+/*
+ * 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 "SkTagList.h"
+
+SkTagList::~SkTagList()
+{
+}
+
+SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag)
+{
+    SkASSERT(tag < kSkTagListCount);
+
+    while (rec != NULL)
+    {
+        if (rec->fTag == tag)
+            break;
+        rec = rec->fNext;
+    }
+    return rec;
+}
+
+void SkTagList::DeleteTag(SkTagList** head, U8CPU tag)
+{
+    SkASSERT(tag < kSkTagListCount);
+
+    SkTagList* rec = *head;
+    SkTagList* prev = NULL;
+
+    while (rec != NULL)
+    {
+        SkTagList* next = rec->fNext;
+
+        if (rec->fTag == tag)
+        {
+            if (prev)
+                prev->fNext = next;
+            else
+                *head = next;
+            delete rec;
+            break;
+        }
+        prev = rec;
+        rec = next;
+    }
+}
+
+void SkTagList::DeleteAll(SkTagList* rec)
+{
+    while (rec)
+    {
+        SkTagList* next = rec->fNext;
+        delete rec;
+        rec = next;
+    }
+}
+
diff --git a/legacy/src/views/SkTagList.h b/legacy/src/views/SkTagList.h
new file mode 100644
index 0000000..47294e3
--- /dev/null
+++ b/legacy/src/views/SkTagList.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 SkTagList_DEFINED
+#define SkTagList_DEFINED
+
+#include "SkTypes.h"
+
+enum SkTagListEnum {
+    kListeners_SkTagList,
+    kViewLayout_SkTagList,
+    kViewArtist_SkTagList,
+
+    kSkTagListCount
+};
+
+struct SkTagList {
+    SkTagList*  fNext;
+    uint16_t    fExtra16;
+    uint8_t     fExtra8;
+    uint8_t     fTag;
+
+    SkTagList(U8CPU tag) : fTag(SkToU8(tag))
+    {
+        SkASSERT(tag < kSkTagListCount);
+        fNext       = NULL;
+        fExtra16    = 0;
+        fExtra8     = 0;
+    }
+    virtual ~SkTagList();
+
+    static SkTagList*   Find(SkTagList* head, U8CPU tag);
+    static void         DeleteTag(SkTagList** headptr, U8CPU tag);
+    static void         DeleteAll(SkTagList* head);
+};
+
+#endif
diff --git a/legacy/src/views/SkTextBox.cpp b/legacy/src/views/SkTextBox.cpp
new file mode 100644
index 0000000..6a88c6c
--- /dev/null
+++ b/legacy/src/views/SkTextBox.cpp
@@ -0,0 +1,229 @@
+
+/*
+ * 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 "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)
+{
+    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;
+
+    while (text < stop)
+    {
+        const char* prevText = text;
+        SkUnichar   uni = SkUTF8_NextUnichar(&text);
+        int         currWS = is_ws(uni);
+        const SkGlyph&  glyph = cache->getUnicharMetrics(uni);
+
+        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)))
+                    text += SkUTF8_CountUTF8Bytes(text);
+            }
+            else    // backup until a whitespace (or 1 char)
+            {
+                if (word_start == start)
+                {
+                    if (prevText > start)
+                        text = prevText;
+                }
+                else
+                    text = word_start;
+            }
+            break;
+        }
+    }
+    return text - start;
+}
+
+int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width)
+{
+    const char* stop = text + len;
+    int         count = 0;
+
+    if (width > 0)
+    {
+        do {
+            count += 1;
+            text += linebreak(text, stop, paint, width);
+        } while (text < stop);
+    }
+    return count;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkTextBox::SkTextBox()
+{
+    fBox.setEmpty();
+    fSpacingMul = SK_Scalar1;
+    fSpacingAdd = 0;
+    fMode = kLineBreak_Mode;
+    fSpacingAlign = kStart_SpacingAlign;
+}
+
+void SkTextBox::setMode(Mode mode)
+{
+    SkASSERT((unsigned)mode < kModeCount);
+    fMode = SkToU8(mode);
+}
+
+void SkTextBox::setSpacingAlign(SpacingAlign align)
+{
+    SkASSERT((unsigned)align < kSpacingAlignCount);
+    fSpacingAlign = SkToU8(align);
+}
+
+void SkTextBox::getBox(SkRect* box) const
+{
+    if (box)
+        *box = fBox;
+}
+
+void SkTextBox::setBox(const SkRect& box)
+{
+    fBox = box;
+}
+
+void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+    fBox.set(left, top, right, bottom);
+}
+
+void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
+{
+    if (mul)
+        *mul = fSpacingMul;
+    if (add)
+        *add = fSpacingAdd;
+}
+
+void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
+{
+    fSpacingMul = mul;
+    fSpacingAdd = add;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint)
+{
+    SkASSERT(canvas && &paint && (text || len == 0));
+
+    SkScalar marginWidth = fBox.width();
+
+    if (marginWidth <= 0 || len == 0)
+        return;
+
+    const char* textStop = text + len;
+
+    SkScalar                x, y, scaledSpacing, height, fontHeight;
+    SkPaint::FontMetrics    metrics;
+
+    switch (paint.getTextAlign()) {
+    case SkPaint::kLeft_Align:
+        x = 0;
+        break;
+    case SkPaint::kCenter_Align:
+        x = SkScalarHalf(marginWidth);
+        break;
+    default:
+        x = marginWidth;
+        break;
+    }
+    x += fBox.fLeft;
+
+    fontHeight = paint.getFontMetrics(&metrics);
+    scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd;
+    height = fBox.height();
+
+    //  compute Y position for first line
+    {
+        SkScalar textHeight = fontHeight;
+
+        if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign)
+        {
+            int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth);
+            SkASSERT(count > 0);
+            textHeight += scaledSpacing * (count - 1);
+        }
+
+        switch (fSpacingAlign) {
+        case kStart_SpacingAlign:
+            y = 0;
+            break;
+        case kCenter_SpacingAlign:
+            y = SkScalarHalf(height - textHeight);
+            break;
+        default:
+            SkASSERT(fSpacingAlign == kEnd_SpacingAlign);
+            y = height - textHeight;
+            break;
+        }
+        y += fBox.fTop - metrics.fAscent;
+    }
+
+    for (;;)
+    {
+        len = linebreak(text, textStop, paint, marginWidth);
+        if (y + metrics.fDescent + metrics.fLeading > 0)
+            canvas->drawText(text, len, x, y, paint);
+        text += len;
+        if (text >= textStop)
+            break;
+        y += scaledSpacing;
+        if (y + metrics.fAscent >= height)
+            break;
+    } 
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) {
+    fText = text;
+    fLen = len;
+    fPaint = &paint;
+}
+
+void SkTextBox::draw(SkCanvas* canvas) {
+    this->draw(canvas, fText, fLen, *fPaint);
+}
+
+int SkTextBox::countLines() const {
+    return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width());
+}
+
+SkScalar SkTextBox::getTextHeight() const {
+    SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
+    return this->countLines() * spacing;
+}
+
diff --git a/legacy/src/views/SkTouchGesture.cpp b/legacy/src/views/SkTouchGesture.cpp
new file mode 100644
index 0000000..31adc74
--- /dev/null
+++ b/legacy/src/views/SkTouchGesture.cpp
@@ -0,0 +1,329 @@
+
+/*
+ * 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 "SkTouchGesture.h"
+#include "SkMatrix.h"
+#include "SkTime.h"
+
+#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER   true
+
+static const float MAX_FLING_SPEED = 1500;
+
+static float pin_max_fling(float speed) {
+    if (speed > MAX_FLING_SPEED) {
+        speed = MAX_FLING_SPEED;
+    }
+    return speed;
+}
+
+static double getseconds() {
+    return SkTime::GetMSecs() * 0.001;
+}
+
+// returns +1 or -1, depending on the sign of x
+// returns +1 if z is zero
+static SkScalar SkScalarSignNonZero(SkScalar x) {
+    SkScalar sign = SK_Scalar1;
+    if (x < 0) {
+        sign = -sign;
+    }
+    return sign;
+}
+
+static void unit_axis_align(SkVector* unit) {
+    const SkScalar TOLERANCE = SkDoubleToScalar(0.15);
+    if (SkScalarAbs(unit->fX) < TOLERANCE) {
+        unit->fX = 0;
+        unit->fY = SkScalarSignNonZero(unit->fY);
+    } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
+        unit->fX = SkScalarSignNonZero(unit->fX);
+        unit->fY = 0;
+    }
+}
+
+void SkFlingState::reset(float sx, float sy) {
+    fActive = true;
+    fDirection.set(sx, sy);
+    fSpeed0 = SkPoint::Normalize(&fDirection);
+    fSpeed0 = pin_max_fling(fSpeed0);
+    fTime0 = getseconds();
+
+    unit_axis_align(&fDirection);
+//    printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
+}
+
+bool SkFlingState::evaluateMatrix(SkMatrix* matrix) {
+    if (!fActive) {
+        return false;
+    }
+
+    const float t =  (float)(getseconds() - fTime0);
+    const float MIN_SPEED = 2;
+    const float K0 = 5;
+    const float K1 = 0.02f;
+    const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1);
+    if (speed <= MIN_SPEED) {
+        fActive = false;
+        return false;
+    }
+    float dist = (fSpeed0 - speed) / K0;
+
+//    printf("---- time %g speed %g dist %g\n", t, speed, dist);
+    float tx = fDirection.fX * dist;
+    float ty = fDirection.fY * dist;
+    if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) {
+        tx = (float)sk_float_round2int(tx);
+        ty = (float)sk_float_round2int(ty);
+    }
+    matrix->setTranslate(tx, ty);
+//    printf("---- evaluate (%g %g)\n", tx, ty);
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkMSec MAX_DBL_TAP_INTERVAL = 300;
+static const float MAX_DBL_TAP_DISTANCE = 100;
+static const float MAX_JITTER_RADIUS = 2;
+
+// if true, then ignore the touch-move, 'cause its probably just jitter
+static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
+    return  sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
+            sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTouchGesture::SkTouchGesture() {
+    this->reset();
+}
+
+SkTouchGesture::~SkTouchGesture() {
+}
+
+void SkTouchGesture::reset() {
+    fTouches.reset();
+    fState = kEmpty_State;
+    fLocalM.reset();
+    fGlobalM.reset();
+
+    fLastUpT = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
+    fLastUpP.set(0, 0);
+}
+
+void SkTouchGesture::flushLocalM() {
+    fGlobalM.postConcat(fLocalM);
+    fLocalM.reset();
+}
+
+const SkMatrix& SkTouchGesture::localM() {
+    if (fFlinger.isActive()) {
+        if (!fFlinger.evaluateMatrix(&fLocalM)) {
+            this->flushLocalM();
+        }
+    }
+    return fLocalM;
+}
+
+void SkTouchGesture::appendNewRec(void* owner, float x, float y) {
+    Rec* rec = fTouches.append();
+    rec->fOwner = owner;
+    rec->fStartX = rec->fPrevX = rec->fLastX = x;
+    rec->fStartY = rec->fPrevY = rec->fLastY = y;
+    rec->fLastT = rec->fPrevT = SkTime::GetMSecs();
+}
+
+void SkTouchGesture::touchBegin(void* owner, float x, float y) {
+//    GrPrintf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
+
+    int index = this->findRec(owner);
+    if (index >= 0) {
+        this->flushLocalM();
+        fTouches.removeShuffle(index);
+        SkDebugf("---- already exists, removing\n");
+    }
+
+    if (fTouches.count() == 2) {
+        return;
+    }
+
+    this->flushLocalM();
+    fFlinger.stop();
+
+    this->appendNewRec(owner, x, y);
+
+    switch (fTouches.count()) {
+        case 1:
+            fState = kTranslate_State;
+            break;
+        case 2:
+            fState = kZoom_State;
+            break;
+        default:
+            break;
+    }
+}
+
+int SkTouchGesture::findRec(void* owner) const {
+    for (int i = 0; i < fTouches.count(); i++) {
+        if (owner == fTouches[i].fOwner) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static float center(float pos0, float pos1) {
+    return (pos0 + pos1) * 0.5f;
+}
+
+static const float MAX_ZOOM_SCALE = 4;
+static const float MIN_ZOOM_SCALE = 0.25f;
+
+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];
+    
+    if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
+        scale = MAX_ZOOM_SCALE / curr;
+    } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
+        scale = MIN_ZOOM_SCALE / curr;
+    }
+    return scale;
+}
+
+void SkTouchGesture::touchMoved(void* owner, float x, float y) {
+//    GrPrintf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
+
+    SkASSERT(kEmpty_State != fState);
+
+    int index = this->findRec(owner);
+    if (index < 0) {
+        // not found, so I guess we should add it...
+        SkDebugf("---- add missing begin\n");
+        this->appendNewRec(owner, x, y);
+        index = fTouches.count() - 1;
+    }
+
+    Rec& rec = fTouches[index];
+
+    // not sure how valuable this is
+    if (fTouches.count() == 2) {
+        if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
+//            GrPrintf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
+            return;
+        }
+    }
+
+    rec.fPrevX = rec.fLastX; rec.fLastX = x;
+    rec.fPrevY = rec.fLastY; rec.fLastY = y;
+    rec.fPrevT = rec.fLastT; rec.fLastT = SkTime::GetMSecs();
+
+    switch (fTouches.count()) {
+        case 1: {
+            float dx = rec.fLastX - rec.fStartX;
+            float dy = rec.fLastY - rec.fStartY;
+            dx = (float)sk_float_round2int(dx);
+            dy = (float)sk_float_round2int(dy);
+            fLocalM.setTranslate(dx, dy);
+        } break;
+        case 2: {
+            SkASSERT(kZoom_State == fState);
+            const Rec& rec0 = fTouches[0];
+            const Rec& rec1 = fTouches[1];
+            
+            float scale = this->computePinch(rec0, rec1);
+            scale = this->limitTotalZoom(scale);
+
+            fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX),
+                                 -center(rec0.fStartY, rec1.fStartY));
+            fLocalM.postScale(scale, scale);
+            fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX),
+                                  center(rec0.fLastY, rec1.fLastY));
+        } break;
+        default:
+            break;
+    }
+}
+
+void SkTouchGesture::touchEnd(void* owner) {
+//    GrPrintf("--- %d touchEnd   %p\n", fTouches.count(), owner);
+
+    int index = this->findRec(owner);
+    if (index < 0) {
+        SkDebugf("--- not found\n");
+        return;
+    }
+
+    const Rec& rec = fTouches[index];
+    if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
+        return;
+    }
+
+    // count() reflects the number before we removed the owner
+    switch (fTouches.count()) {
+        case 1: {
+            this->flushLocalM();
+            float dx = rec.fLastX - rec.fPrevX;
+            float dy = rec.fLastY - rec.fPrevY;
+            float dur = (rec.fLastT - rec.fPrevT) * 0.001f;
+            if (dur > 0) {
+                fFlinger.reset(dx / dur, dy / dur);
+            }
+            fState = kEmpty_State;
+        } break;
+        case 2:
+            this->flushLocalM();
+            SkASSERT(kZoom_State == fState);
+            fState = kEmpty_State;
+            break;
+        default:
+            SkASSERT(kZoom_State == fState);
+            break;
+    }
+
+    fTouches.removeShuffle(index);
+}
+
+float SkTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
+    double dx = rec0.fStartX - rec1.fStartX;
+    double dy = rec0.fStartY - rec1.fStartY;
+    double dist0 = sqrt(dx*dx + dy*dy);
+
+    dx = rec0.fLastX - rec1.fLastX;
+    dy = rec0.fLastY - rec1.fLastY;
+    double dist1 = sqrt(dx*dx + dy*dy);
+
+    double scale = dist1 / dist0;
+    return (float)scale;
+}
+
+bool SkTouchGesture::handleDblTap(float x, float y) {
+    bool found = false;
+    SkMSec now = SkTime::GetMSecs();
+    if (now - fLastUpT <= MAX_DBL_TAP_INTERVAL) {
+        if (SkPoint::Length(fLastUpP.fX - x,
+                            fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
+            fFlinger.stop();
+            fLocalM.reset();
+            fGlobalM.reset();
+            fTouches.reset();
+            fState = kEmpty_State;
+            found = true;
+        }
+    }
+
+    fLastUpT = now;
+    fLastUpP.set(x, y);
+    return found;
+}
+
+
diff --git a/legacy/src/views/SkView.cpp b/legacy/src/views/SkView.cpp
new file mode 100644
index 0000000..fc1ddfb
--- /dev/null
+++ b/legacy/src/views/SkView.cpp
@@ -0,0 +1,823 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkView.h"
+#include "SkCanvas.h"
+
+////////////////////////////////////////////////////////////////////////
+
+SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
+{
+	fWidth = fHeight = 0;
+	fLoc.set(0, 0);
+	fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
+    fMatrix.setIdentity();
+	fContainsFocus = 0;
+}
+
+SkView::~SkView()
+{
+	this->detachAllChildren();
+}
+
+void SkView::setFlags(uint32_t flags)
+{
+	SkASSERT((flags & ~kAllFlagMasks) == 0);
+
+	uint32_t diff = fFlags ^ flags;
+
+	if (diff & kVisible_Mask)
+		this->inval(NULL);
+
+	fFlags = SkToU8(flags);
+
+	if (diff & kVisible_Mask)
+	{
+		this->inval(NULL);
+	}
+}
+
+void SkView::setVisibleP(bool pred)
+{
+	this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
+}
+
+void SkView::setEnabledP(bool pred)
+{
+	this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
+}
+
+void SkView::setFocusableP(bool pred)
+{
+	this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
+}
+
+void SkView::setClipToBounds(bool pred) {
+    this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift));
+}
+
+void SkView::setSize(SkScalar width, SkScalar 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();
+	}
+}
+
+void SkView::setLoc(SkScalar x, SkScalar 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);
+}
+
+void SkView::setLocalMatrix(const SkMatrix& matrix) 
+{
+    this->inval(NULL);
+    fMatrix = matrix;
+    this->inval(NULL);
+}
+
+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)) {
+                return;
+        }
+
+		SkAutoCanvasRestore	as(canvas, true);
+
+        if (this->isClipToBounds()) {
+            canvas->clipRect(r);
+        }
+        
+        canvas->translate(fLoc.fX, fLoc.fY);		
+        canvas->concat(fMatrix);
+        
+        if (fParent) {
+            fParent->beforeChild(this, canvas);
+        }
+
+        int sc = canvas->save();
+		this->onDraw(canvas);
+        canvas->restoreToCount(sc);
+
+        if (fParent) {
+            fParent->afterChild(this, canvas);
+        }
+        
+		B2FIter	iter(this);
+		SkView*	child;
+
+        SkCanvas* childCanvas = this->beforeChildren(canvas);
+
+		while ((child = iter.next()) != NULL)
+			child->draw(childCanvas);
+        
+        this->afterChildren(canvas);
+	}
+}
+
+void SkView::inval(SkRect* rect) {
+	SkView*	view = this;
+    SkRect storage;
+
+	for (;;) {
+        if (!view->isVisible()) {
+            return;
+        }
+        if (view->isClipToBounds()) {
+            SkRect bounds;
+            view->getLocalBounds(&bounds);
+            if (rect && !bounds.intersect(*rect)) {
+                return;
+            }
+            storage = bounds;
+            rect = &storage;
+        }
+        if (view->handleInval(rect)) {
+            return;
+        }
+
+		SkView* parent = view->fParent;
+        if (parent == NULL) {
+            return;
+        }
+
+        if (rect) {
+            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* SkView::getFocusView() const
+{
+	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();
+}
+
+bool SkView::acceptFocus()
+{
+	return this->isFocusable() && this->setFocusView(this);
+}
+
+/*
+	Try to give focus to this view, or its children
+*/
+SkView* SkView::acceptFocus(FocusDirection dir)
+{
+	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;
+
+		if (this->acceptFocus())
+			return this;
+	}
+
+	return NULL;
+}
+
+SkView* SkView::moveFocus(FocusDirection dir)
+{
+	SkView* focus = this->getFocusView();
+
+	if (focus == NULL)
+	{	// start with the root
+		focus = this;
+		while (focus->fParent)
+			focus = focus->fParent;
+	}
+
+	SkView*	child, *parent;
+
+	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;
+
+				child = parent;
+				parent = parent->fParent;
+			}
+		}
+	}
+	return NULL;
+}
+
+void SkView::onFocusChange(bool gainFocusP)
+{
+	this->inval(NULL);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkView::Click::Click(SkView* target)
+{
+    SkASSERT(target);
+    fTargetID = target->getSinkID();
+    fType = NULL;
+    fWeOwnTheType = false;
+    fOwner = NULL;
+}
+
+SkView::Click::~Click()
+{
+	this->resetType();
+}
+
+void SkView::Click::resetType()
+{
+	if (fWeOwnTheType)
+	{
+		sk_free(fType);
+		fWeOwnTheType = false;
+	}
+	fType = NULL;
+}
+
+bool SkView::Click::isType(const char type[]) const
+{
+	const char* t = fType;
+
+	if (type == t)
+		return true;
+
+	if (type == NULL)
+		type = "";
+	if (t == NULL)
+		t = "";
+	return !strcmp(t, type);
+}
+
+void SkView::Click::setType(const 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;
+		}
+	}
+}
+
+SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
+{
+	if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
+		return NULL;
+    }
+
+    if (this->onSendClickToChildren(x, y)) {
+        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 (click) {
+                return click;
+            }
+        }
+    }
+	return this->onFindClickHandler(x, y);
+}
+
+void SkView::DoClickDown(Click* click, int x, int y)
+{
+	SkASSERT(click);
+
+	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+	if (target == NULL)
+		return;
+
+	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->fState = Click::kDown_State;
+	target->onClick(click);
+}
+
+void SkView::DoClickMoved(Click* click, int x, int y)
+{
+	SkASSERT(click);
+
+	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+	if (target == NULL)
+		return;
+
+	click->fIPrev = click->fICurr;
+	click->fICurr.set(x, y);
+
+	click->fPrev = click->fCurr;
+	click->fCurr.iset(x, y);
+	target->globalToLocal(&click->fCurr);
+
+	click->fState = Click::kMoved_State;
+	target->onClick(click);
+}
+
+void SkView::DoClickUp(Click* click, int x, int y)
+{
+	SkASSERT(click);
+
+	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+	if (target == NULL)
+		return;
+
+	click->fIPrev = click->fICurr;
+	click->fICurr.set(x, y);
+
+	click->fPrev = click->fCurr;
+	click->fCurr.iset(x, y);
+	target->globalToLocal(&click->fCurr);
+
+	click->fState = Click::kUp_State;
+	target->onClick(click);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::invokeLayout() {
+	SkView::Layout* layout = this->getLayout();
+
+	if (layout) {
+		layout->layoutChildren(this);
+    }
+}
+
+void SkView::onDraw(SkCanvas* canvas) {
+	Artist* artist = this->getArtist();
+
+	if (artist) {
+		artist->draw(this, canvas);
+    }
+}
+
+void SkView::onSizeChange() {}
+
+bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
+    return true;
+}
+
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
+	return NULL;
+}
+
+bool SkView::onClick(Click*) {
+	return false;
+}
+
+bool SkView::handleInval(const SkRect*) {
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::getLocalBounds(SkRect* bounds) const
+{
+	if (bounds)
+		bounds->set(0, 0, fWidth, fHeight);
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+void SkView::detachFromParent_NoLayout()
+{
+	if (fParent == NULL)
+		return;
+
+	if (fContainsFocus)
+		(void)this->setFocusView(NULL);
+
+	this->inval(NULL);
+
+	SkView*	next = NULL;
+
+	if (fNextSibling != this)	// do we have any siblings
+	{
+		fNextSibling->fPrevSibling = fPrevSibling;
+		fPrevSibling->fNextSibling = fNextSibling;
+		next = fNextSibling;
+	}
+
+	if (fParent->fFirstChild == this)
+		fParent->fFirstChild = next;
+
+	fParent = fNextSibling = fPrevSibling = NULL;
+
+	this->unref();
+}
+
+void SkView::detachFromParent()
+{
+	SkView* parent = fParent;
+
+	if (parent)
+	{
+		this->detachFromParent_NoLayout();
+		parent->invokeLayout();
+	}
+}
+
+SkView* SkView::attachChildToBack(SkView* child)
+{
+	SkASSERT(child != this);
+
+	if (child == NULL || fFirstChild == child)
+		goto DONE;
+
+	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;
+	}
+
+	fFirstChild = child;
+	child->fParent = this;
+	child->inval(NULL);
+
+	this->invokeLayout();
+DONE:
+	return child;
+}
+
+SkView* SkView::attachChildToFront(SkView* child)
+{
+	SkASSERT(child != this);
+
+	if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
+		goto DONE;
+
+	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;
+	}
+
+	child->fParent = this;
+	child->inval(NULL);
+
+	this->invokeLayout();
+DONE:
+	return child;
+}
+
+void SkView::detachAllChildren()
+{
+	while (fFirstChild)
+		fFirstChild->detachFromParent_NoLayout();
+}
+
+void SkView::localToGlobal(SkMatrix* matrix) const
+{
+    if (matrix) {
+        matrix->reset();
+        const SkView* view = this;
+        while (view)
+        {
+            matrix->preConcat(view->getLocalMatrix());
+            matrix->preTranslate(-view->fLoc.fX, -view->fLoc.fY);
+            view = view->fParent;
+        }
+    }
+}
+void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
+{
+	SkASSERT(this);
+	if (local)
+	{
+        SkMatrix m;
+        this->localToGlobal(&m);
+        SkPoint p;
+        m.invert(&m);
+        m.mapXY(x, y, &p);
+		local->set(p.fX, p.fY);
+	}
+}
+
+//////////////////////////////////////////////////////////////////
+
+/*	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;
+
+	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);
+
+	// inflate the flags
+
+	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);
+}
+
+void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->onInflate(dom, node);
+}
+
+void SkView::onPostInflate(const SkTDict<SkView*>&)
+{
+	// override in subclass as needed
+}
+
+void SkView::postInflate(const SkTDict<SkView*>& dict)
+{
+	this->onPostInflate(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* SkView::sendQueryToParents(SkEvent* evt) {
+	SkView* parent = fParent;
+    
+	while (parent) {
+		if (parent->doQuery(evt)) {
+			return parent;
+        }
+		parent = parent->fParent;
+	}
+	return NULL;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkView::F2BIter::F2BIter(const SkView* parent)
+{
+	fFirstChild = parent ? parent->fFirstChild : NULL;
+	fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
+}
+
+SkView*	SkView::F2BIter::next()
+{
+	SkView* curr = fChild;
+
+	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;
+}
+
+SkView*	SkView::B2FIter::next()
+{
+	SkView* curr = fChild;
+
+	if (fChild)
+	{
+		SkView* next = fChild->fNextSibling;
+		if (next == fFirstChild)
+			next = NULL;
+		fChild = next;
+	}
+	return curr;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static inline void show_if_nonzero(const char name[], SkScalar value)
+{
+	if (value)
+		SkDebugf("%s=\"%g\"", name, value/65536.);
+}
+
+static void tab(int level)
+{
+	for (int i = 0; i < level; i++)
+		SkDebugf("    ");
+}
+
+static void dumpview(const SkView* view, int level, bool recurse)
+{
+	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());
+
+	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);
+		}
+
+		if (!noChildren)
+		{
+			tab(level);
+			SkDebugf("</view>\n");
+		}
+		else
+			goto ONELINER;
+	}
+	else
+	{
+	ONELINER:
+		SkDebugf(" />\n");
+	}
+}
+
+void SkView::dump(bool recurse) const
+{
+	dumpview(this, 0, recurse);
+}
+
+#endif
diff --git a/legacy/src/views/SkViewInflate.cpp b/legacy/src/views/SkViewInflate.cpp
new file mode 100644
index 0000000..184e540
--- /dev/null
+++ b/legacy/src/views/SkViewInflate.cpp
@@ -0,0 +1,146 @@
+
+/*
+ * 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 "SkViewInflate.h"
+#include "SkView.h"
+#include <stdio.h>
+
+SkViewInflate::SkViewInflate() : fIDs(kMinIDStrAlloc), fStrings(kMinIDStrAlloc)
+{
+}
+
+SkViewInflate::~SkViewInflate()
+{
+}
+
+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 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, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL)
+				this->addIDStr(&fBroadcastTo, parent, target);
+		}
+		child = dom.getNextSibling(child);
+	}
+
+	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);
+}
+
+SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root)
+{
+	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);
+
+	// 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());
+		}
+	}
+
+	// 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);
+
+	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;
+}
+
+SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node)
+{
+	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);
+}
+
+#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);
+
+	iter = fBroadcastTo.begin();
+	stop = fBroadcastTo.end();
+	for (; iter < stop; iter++)
+		SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
+}
+#endif
+
diff --git a/legacy/src/views/SkViewPriv.cpp b/legacy/src/views/SkViewPriv.cpp
new file mode 100644
index 0000000..ad15f3f
--- /dev/null
+++ b/legacy/src/views/SkViewPriv.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 "SkViewPriv.h"
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::Artist::draw(SkView* view, SkCanvas* 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);
+}
+
+void SkView::Artist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	// 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);
+
+	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 (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);
+}
+
+void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* 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
+}
+
+SkView::Layout* SkView::getLayout() const
+{
+	Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+	SkASSERT(rec == NULL || 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 (rec)
+			SkRefCnt_SafeAssign(rec->fLayout, obj);
+		else
+			this->addTagList(new Layout_SkTagList(obj));
+	}
+	
+	if (invokeLayoutNow)
+		this->invokeLayout();
+
+	return obj;
+}
+
diff --git a/legacy/src/views/SkViewPriv.h b/legacy/src/views/SkViewPriv.h
new file mode 100644
index 0000000..d8cf966
--- /dev/null
+++ b/legacy/src/views/SkViewPriv.h
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkViewPriv_DEFINED
+#define SkViewPriv_DEFINED
+
+#include "SkView.h"
+#include "SkTagList.h"
+
+struct Layout_SkTagList : SkTagList {
+	SkView::Layout*	fLayout;
+
+	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;
+
+	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/legacy/src/views/SkWidget.cpp
similarity index 100%
rename from src/views/SkWidget.cpp
rename to legacy/src/views/SkWidget.cpp
diff --git a/src/views/SkWidgetViews.cpp b/legacy/src/views/SkWidgetViews.cpp
similarity index 100%
rename from src/views/SkWidgetViews.cpp
rename to legacy/src/views/SkWidgetViews.cpp
diff --git a/legacy/src/views/SkWidgets.cpp b/legacy/src/views/SkWidgets.cpp
new file mode 100644
index 0000000..69d755b
--- /dev/null
+++ b/legacy/src/views/SkWidgets.cpp
@@ -0,0 +1,561 @@
+
+/*
+ * 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 "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+#include "SkTextBox.h"
+
+#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);
+	}
+#else
+	#define assert_no_attr(dom, node, attr)
+#endif
+
+#include "SkAnimator.h"
+#include "SkTime.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum SkinType {
+	kPushButton_SkinType,
+	kStaticText_SkinType,
+
+	kSkinTypeCount
+};
+
+struct SkinSuite {
+	SkinSuite();
+	~SkinSuite()
+	{
+		for (int i = 0; i < kSkinTypeCount; i++)
+			delete fAnimators[i];
+	}
+
+	SkAnimator*	get(SkinType);
+
+private:
+	SkAnimator*	fAnimators[kSkinTypeCount];
+};
+
+SkinSuite::SkinSuite()
+{
+	static const char kSkinPath[] = "skins/";
+
+	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);
+
+		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;
+		}
+	}
+}
+
+SkAnimator* SkinSuite::get(SkinType st)
+{
+	SkASSERT((unsigned)st < kSkinTypeCount);
+	return fAnimators[st];
+}
+
+static SkinSuite* gSkinSuite;
+
+static SkAnimator* get_skin_animator(SkinType st)
+{
+#if 0
+	if (gSkinSuite == NULL)
+		gSkinSuite = new SkinSuite;
+	return gSkinSuite->get(st);
+#else
+	return NULL;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkWidget::Init()
+{
+}
+
+void SkWidget::Term()
+{
+	delete gSkinSuite;
+}
+
+void SkWidget::onEnabledChange()
+{
+	this->inval(NULL);
+}
+
+void SkWidget::postWidgetEvent()
+{
+	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
+}
+
+void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(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();
+}
+
+size_t SkHasLabelWidget::getLabel(char buffer[]) const
+{
+	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());
+}
+
+void SkHasLabelWidget::setLabel(const char 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();
+	}
+}
+
+void SkHasLabelWidget::onLabelChange()
+{
+	// override in subclass
+}
+
+void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+
+	const char* text = dom.findAttr(node, "label");
+	if (text)
+		this->setLabel(text);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkButtonWidget::setButtonState(State state)
+{
+	if (fState != state)
+	{
+		fState = state;
+		this->onButtonStateChange();
+	}
+}
+
+void SkButtonWidget::onButtonStateChange()
+{
+	this->inval(NULL);
+}
+
+void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+
+	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);
+}
+
+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";
+}
+
+#include "SkBlurMaskFilter.h"
+#include "SkEmbossMaskFilter.h"
+
+static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed)
+{
+	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;
+
+	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();
+}
+
+void SkPushButtonWidget::onDraw(SkCanvas* canvas)
+{
+	this->INHERITED::onDraw(canvas);
+
+	SkString label;
+	this->getLabel(&label);
+
+	SkAnimator* anim = get_skin_animator(kPushButton_SkinType);
+
+	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()));
+
+		(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);
+
+		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());
+
+//		if (this->getButtonState() == kOn_State)
+//			p.setColor(SK_ColorRED);
+//		else
+			p.setColor(SK_ColorWHITE);
+
+		box.draw(canvas, label.c_str(), label.size(), p);
+	}
+}
+
+SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y)
+{
+	this->acceptFocus();
+	return new Click(this);
+}
+
+bool SkPushButtonWidget::onClick(Click* click)
+{
+	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;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags)
+{
+	fMargin.set(0, 0);
+	fMode = kFixedSize_Mode;
+	fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+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;
+
+		SkScalar	before, after;
+		(void)fPaint.measureText(0, NULL, &before, &after);
+
+		this->setHeight(lines * (after - before) + 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[])
+{
+	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)
+{
+	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, "mode");
+
+	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)
+		SkPaint_Inflate(&fPaint, dom, node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+
+SkBitmapView::SkBitmapView(U32 flags) : SkView(flags)
+{
+}
+
+SkBitmapView::~SkBitmapView()
+{
+}
+
+bool SkBitmapView::getBitmap(SkBitmap* bitmap) const
+{
+	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);
+	}
+}
+
+bool SkBitmapView::loadBitmapFromFile(const char path[])
+{
+	SkBitmap	bitmap;
+
+	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;
+
+		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);
+
+	const char* src = dom.findAttr(node, "src");
+	if (src)
+		(void)this->loadBitmapFromFile(src);
+}
+
+#endif
+
diff --git a/legacy/src/views/SkWindow.cpp b/legacy/src/views/SkWindow.cpp
new file mode 100644
index 0000000..c952a61
--- /dev/null
+++ b/legacy/src/views/SkWindow.cpp
@@ -0,0 +1,416 @@
+
+/*
+ * 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 "SkWindow.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkOSMenu.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+#define SK_EventDelayInval "\xd" "n" "\xa" "l"
+
+#define TEST_BOUNDERx
+
+#include "SkBounder.h"
+class test_bounder : public SkBounder {
+public:
+	test_bounder(const SkBitmap& bm) : fCanvas(bm) {}
+protected:
+	virtual bool onIRect(const SkIRect& r)
+	{
+		SkRect	rr;
+
+		rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop),
+				SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom));
+
+		SkPaint	p;
+
+		p.setStyle(SkPaint::kStroke_Style);
+		p.setColor(SK_ColorYELLOW);
+
+#if 0
+		rr.inset(SK_ScalarHalf, SK_ScalarHalf);
+#else
+		rr.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+#endif
+
+		fCanvas.drawRect(rr, p);
+		return true;
+	}
+private:
+	SkCanvas	fCanvas;
+};
+
+SkWindow::SkWindow() : fFocusView(NULL)
+{
+    fClicks.reset();
+    fWaitingOnInval = false;
+
+#ifdef SK_BUILD_FOR_WINCE
+    fConfig = SkBitmap::kRGB_565_Config;
+#else
+    fConfig = SkBitmap::kARGB_8888_Config;
+#endif
+
+    fMatrix.reset();
+}
+
+SkWindow::~SkWindow()
+{
+    fClicks.deleteAll();
+    fMenus.deleteAll();
+}
+
+void SkWindow::setMatrix(const SkMatrix& matrix) {
+    if (fMatrix != matrix) {
+        fMatrix = matrix;
+        this->inval(NULL);
+    }
+}
+
+void SkWindow::preConcat(const SkMatrix& matrix) {
+    SkMatrix m;
+    m.setConcat(fMatrix, matrix);
+    this->setMatrix(m);
+}
+
+void SkWindow::postConcat(const SkMatrix& matrix) {
+    SkMatrix m;
+    m.setConcat(matrix, fMatrix);
+    this->setMatrix(m);
+}
+
+void SkWindow::setConfig(SkBitmap::Config 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 (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);
+	}
+}
+
+void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+	fBitmap.eraseARGB(a, r, g, b);
+}
+
+void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b)
+{
+	fBitmap.eraseRGB(r, g, b);
+}
+
+bool SkWindow::handleInval(const SkRect* localR)
+{
+	SkIRect	ir;
+
+    if (localR) {
+        SkRect devR;
+        SkMatrix inverse;
+        if (!fMatrix.invert(&inverse)) {
+            return false;
+        }
+        fMatrix.mapRect(&devR, *localR);
+        devR.round(&ir);
+    } else {
+        ir.set(0, 0,
+			   SkScalarRound(this->width()),
+			   SkScalarRound(this->height()));
+    }
+	fDirtyRgn.op(ir, SkRegion::kUnion_Op);
+
+	this->onHandleInval(ir);
+	return true;
+}
+
+void SkWindow::forceInvalAll() {
+    fDirtyRgn.setRect(0, 0,
+                      SkScalarCeil(this->width()),
+                      SkScalarCeil(this->height()));
+}
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+	#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)
+{
+	if (!fDirtyRgn.isEmpty())
+	{
+		SkBitmap bm = this->getBitmap();
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+		char* buffer = (char*)GXBeginDraw();
+		SkASSERT(buffer);
+
+		RECT	rect;
+		GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
+		buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
+
+		bm.setPixels(buffer);
+#endif
+
+		SkCanvas	rasterCanvas;
+
+        if (NULL == canvas) {
+            canvas = &rasterCanvas;
+        }
+        canvas->setBitmapDevice(bm);
+
+		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();
+
+#ifdef TEST_BOUNDER
+		test_bounder	b(bm);
+		canvas->setBounder(&b);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+		gEnableControlledThrow = true;
+#endif
+#ifdef SK_BUILD_FOR_WIN32
+		//try {
+			this->draw(canvas);
+		//}
+		//catch (...) {
+		//}
+#else
+		this->draw(canvas);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+		gEnableControlledThrow = false;
+#endif
+#ifdef TEST_BOUNDER
+		canvas->setBounder(NULL);
+#endif
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+		GXEndDraw();
+#endif
+
+		return true;
+	}
+	return false;
+}
+
+bool SkWindow::handleChar(SkUnichar uni)
+{
+	if (this->onHandleChar(uni))
+		return true;
+
+	SkView* focus = this->getFocusView();
+	if (focus == NULL)
+		focus = this;
+
+	SkEvent evt(SK_EventType_Unichar);
+	evt.setFast32(uni);
+	return focus->doEvent(evt);
+}
+
+bool SkWindow::handleKey(SkKey key)
+{
+	if (key == kNONE_SkKey)
+		return false;
+
+	if (this->onHandleKey(key))
+		return true;
+
+	// 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;
+	}
+
+	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);
+        if (focus->doEvent(evt))
+            return true;
+    }
+    return false;
+}
+
+void SkWindow::addMenu(SkOSMenu* menu) {
+	*fMenus.append() = menu;
+	this->onAddMenu(menu);
+}
+
+void SkWindow::setTitle(const char title[]) {
+    if (NULL == title) {
+        title = "";
+    }
+    fTitle.set(title);
+    this->onSetTitle(title);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+bool SkWindow::onEvent(const SkEvent& evt)
+{
+	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);
+}
+
+bool SkWindow::onGetFocusView(SkView** focus) const
+{
+	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;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkWindow::onHandleInval(const SkIRect&)
+{
+}
+
+bool SkWindow::onHandleChar(SkUnichar)
+{
+	return false;
+}
+
+bool SkWindow::onHandleKey(SkKey key)
+{
+	return false;
+}
+
+bool SkWindow::onHandleKeyUp(SkKey key)
+{
+    return false;
+}
+
+bool SkWindow::handleClick(int x, int y, Click::State state, void *owner) {
+    return this->onDispatchClick(x, y, state, owner);
+}
+
+bool SkWindow::onDispatchClick(int x, int y, Click::State state,
+        void* owner) {
+	bool handled = false;
+
+    // First, attempt to find an existing click with this owner.
+    int index = -1;
+    for (int i = 0; i < fClicks.count(); i++) {
+        if (owner == fClicks[i]->fOwner) {
+            index = i;
+            break;
+        }
+    }
+
+	switch (state) {
+        case Click::kDown_State: {
+            if (index != -1) {
+                delete fClicks[index];
+                fClicks.remove(index);
+            }
+            Click* click = this->findClickHandler(SkIntToScalar(x),
+                    SkIntToScalar(y));
+
+            if (click) {
+                click->fOwner = owner;
+                *fClicks.append() = click;
+                SkView::DoClickDown(click, x, y);
+                handled = true;
+            }
+            break;
+        }
+        case Click::kMoved_State:
+            if (index != -1) {
+                SkView::DoClickMoved(fClicks[index], x, y);
+                handled = true;
+            }
+            break;
+        case Click::kUp_State:
+            if (index != -1) {
+                SkView::DoClickUp(fClicks[index], x, y);
+                delete fClicks[index];
+                fClicks.remove(index);
+                handled = true;
+            }
+            break;
+        default:
+            // Do nothing
+            break;
+	}
+	return handled;
+}
+
diff --git a/legacy/src/views/views_files.mk b/legacy/src/views/views_files.mk
new file mode 100644
index 0000000..9c5e9e0
--- /dev/null
+++ b/legacy/src/views/views_files.mk
@@ -0,0 +1,26 @@
+SOURCE := \
+    SkEvent.cpp \
+    SkEventSink.cpp \
+    SkOSMenu.cpp \
+    SkTagList.cpp \
+    SkView.cpp \
+    SkViewPriv.cpp \
+    SkWindow.cpp \
+    SkTouchGesture.cpp
+#    SkBGViewArtist.cpp \
+    SkMetaData.cpp \
+    SkListView.cpp \
+    SkListWidget.cpp \
+    SkParsePaint.cpp \
+    SkProgressBarView.cpp \
+    SkProgressView.cpp \
+    SkScrollBarView.cpp \
+    SkStackViewLayout.cpp \
+    SkStaticTextView.cpp \
+    SkTextBox.cpp \
+    SkViewInflate.cpp \
+    SkWidget.cpp \
+    SkWidgetViews.cpp \
+    SkWidgets.cpp \
+#    SkBorderView.cpp \
+#    SkImageView.cpp \
diff --git a/legacy/src/xml/SkBML_Verbs.h b/legacy/src/xml/SkBML_Verbs.h
new file mode 100644
index 0000000..709764d
--- /dev/null
+++ b/legacy/src/xml/SkBML_Verbs.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 SkBML_Verbs_DEFINED
+#define SkBML_Verbs_DEFINED
+
+enum Verbs {
+    kStartElem_Value_Verb,
+    kStartElem_Index_Verb,
+    kEndElem_Verb,
+    kAttr_Value_Value_Verb,
+    kAttr_Value_Index_Verb,
+    kAttr_Index_Value_Verb,
+    kAttr_Index_Index_Verb,
+
+    kVerbCount
+};
+
+#endif // SkBML_Verbs_DEFINED
diff --git a/legacy/src/xml/SkBML_XMLParser.cpp b/legacy/src/xml/SkBML_XMLParser.cpp
new file mode 100644
index 0000000..dbdfe4b
--- /dev/null
+++ b/legacy/src/xml/SkBML_XMLParser.cpp
@@ -0,0 +1,184 @@
+
+/*
+ * 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 "SkBML_XMLParser.h"
+#include "SkBML_Verbs.h"
+#include "SkStream.h"
+#include "SkXMLWriter.h"
+
+static uint8_t rbyte(SkStream& s)
+{
+    uint8_t b;
+    size_t size = s.read(&b, 1);
+    SkASSERT(size == 1);
+    return b;
+}
+
+static int rdata(SkStream& s, int data)
+{
+    SkASSERT((data & ~31) == 0);
+    if (data == 31)
+    {
+        data = rbyte(s);
+        if (data == 0xFF)
+        {
+            data = rbyte(s);
+            data = (data << 8) | rbyte(s);
+        }
+    }
+    return data;
+}
+
+static void set(char* array[256], int index, SkStream& s, int data)
+{
+    SkASSERT((unsigned)index <= 255);
+
+    size_t size = rdata(s, data);
+
+    if (array[index] == NULL)
+        array[index] = (char*)sk_malloc_throw(size + 1);
+    else
+    {
+        if (strlen(array[index]) < size)
+            array[index] = (char*)sk_realloc_throw(array[index], size + 1);
+    }
+
+    s.read(array[index], size);
+    array[index][size] = 0;
+}
+
+static void freeAll(char* array[256])
+{
+    for (int i = 0; i < 256; i++)
+        sk_free(array[i]);
+}
+
+struct BMLW {
+    char*   fElems[256];
+    char*   fNames[256];
+    char*   fValues[256];
+
+    // important that these are uint8_t, so we get automatic wrap-around
+    uint8_t  fNextElem, fNextName, fNextValue;
+
+    BMLW()
+    {
+        memset(fElems, 0, sizeof(fElems));
+        memset(fNames, 0, sizeof(fNames));
+        memset(fValues, 0, sizeof(fValues));
+
+        fNextElem = fNextName = fNextValue = 0;
+    }
+    ~BMLW()
+    {
+        freeAll(fElems);
+        freeAll(fNames);
+        freeAll(fValues);
+    }
+};
+
+static void rattr(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer)
+{
+    int data = verb & 31;
+    verb >>= 5;
+
+    int nameIndex, valueIndex;
+
+    switch (verb) {
+    case kAttr_Value_Value_Verb:
+        nameIndex = rec.fNextName;      // record before the ++
+        set(rec.fNames, rec.fNextName++, s, data);
+        valueIndex = rec.fNextValue;    // record before the ++
+        set(rec.fValues, rec.fNextValue++, s, 31);
+        break;
+    case kAttr_Value_Index_Verb:
+        nameIndex = rec.fNextName;      // record before the ++
+        set(rec.fNames, rec.fNextName++, s, data);
+        valueIndex = rbyte(s);
+        break;
+    case kAttr_Index_Value_Verb:
+        nameIndex = rdata(s, data);
+        valueIndex = rec.fNextValue;    // record before the ++
+        set(rec.fValues, rec.fNextValue++, s, 31);
+        break;
+    case kAttr_Index_Index_Verb:
+        nameIndex = rdata(s, data);
+        valueIndex = rbyte(s);
+        break;
+    default:
+        SkDEBUGFAIL("bad verb");
+        return;
+    }
+    writer.addAttribute(rec.fNames[nameIndex], rec.fValues[valueIndex]);
+}
+
+static void relem(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer)
+{
+    int data = verb & 31;
+    verb >>= 5;
+
+    int elemIndex;
+
+    if (verb == kStartElem_Value_Verb)
+    {
+        elemIndex = rec.fNextElem;      // record before the ++
+        set(rec.fElems, rec.fNextElem++, s, data);
+    }
+    else
+    {
+        SkASSERT(verb == kStartElem_Index_Verb);
+        elemIndex = rdata(s, data);
+    }
+
+    writer.startElement(rec.fElems[elemIndex]);
+
+    for (;;)
+    {
+        verb = rbyte(s);
+        switch (verb >> 5) {
+        case kAttr_Value_Value_Verb:
+        case kAttr_Value_Index_Verb:
+        case kAttr_Index_Value_Verb:
+        case kAttr_Index_Index_Verb:
+            rattr(verb, s, rec, writer);
+            break;
+        case kStartElem_Value_Verb:
+        case kStartElem_Index_Verb:
+            relem(verb, s, rec, writer);
+            break;
+        case kEndElem_Verb:
+            writer.endElement();
+            return;
+        default:
+            SkDEBUGFAIL("bad verb");
+        }
+    }
+}
+
+void BML_XMLParser::Read(SkStream& s, SkXMLWriter& writer)
+{
+    BMLW rec;
+    writer.writeHeader();
+    relem(rbyte(s), s, rec, writer);
+}
+
+void BML_XMLParser::Read(SkStream& s, SkWStream& output)
+{
+    SkXMLStreamWriter writer(&output);
+    Read(s, writer);
+}
+
+void BML_XMLParser::Read(SkStream& s, SkXMLParser& output)
+{
+    SkXMLParserWriter writer(&output);
+    Read(s, writer);
+}
+
+
+
diff --git a/legacy/src/xml/SkDOM.cpp b/legacy/src/xml/SkDOM.cpp
new file mode 100644
index 0000000..474c291
--- /dev/null
+++ b/legacy/src/xml/SkDOM.cpp
@@ -0,0 +1,504 @@
+
+/*
+ * 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 "SkDOM.h"
+
+/////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+
+bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node)
+{
+    const char* elemName = dom.getName(node);
+
+    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;
+
+    if ((node = dom.getFirstChild(node)) != NULL)
+        do {
+            if (!this->parse(dom, node))
+                return false;
+        } while ((node = dom.getNextSibling(node)) != NULL);
+    
+    return !this->endElement(elemName);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+struct SkDOMAttr {
+    const char* fName;
+    const char* fValue;
+};
+
+struct SkDOMNode {
+    const char* fName;
+    SkDOMNode*  fFirstChild;
+    SkDOMNode*  fNextSibling;
+    uint16_t    fAttrCount;
+    uint8_t     fType;
+    uint8_t     fPad;
+
+    const SkDOMAttr* attrs() const
+    {
+        return (const SkDOMAttr*)(this + 1);
+    }
+    SkDOMAttr* attrs()
+    {
+        return (SkDOMAttr*)(this + 1);
+    }
+};
+
+/////////////////////////////////////////////////////////////////////////
+
+#define kMinChunkSize   512
+
+SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL)
+{
+}
+
+SkDOM::~SkDOM()
+{
+}
+
+const SkDOM::Node* SkDOM::getRootNode() const
+{
+    return fRoot;
+}
+
+const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Node* child = node->fFirstChild;
+
+    if (name)
+    {
+        for (; child != NULL; child = child->fNextSibling)
+            if (!strcmp(name, child->fName))
+                break;
+    }
+    return child;
+}
+
+const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Node* sibling = node->fNextSibling;
+    if (name)
+    {
+        for (; sibling != NULL; sibling = sibling->fNextSibling)
+            if (!strcmp(name, sibling->fName))
+                break;
+    }
+    return sibling;
+}
+
+SkDOM::Type SkDOM::getType(const Node* node) const
+{
+    SkASSERT(node);
+    return (Type)node->fType;
+}
+
+const char* SkDOM::getName(const Node* node) const
+{
+    SkASSERT(node);
+    return node->fName;
+}
+
+const char* SkDOM::findAttr(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Attr* attr = node->attrs();
+    const Attr* stop = attr + node->fAttrCount;
+
+    while (attr < stop)
+    {
+        if (!strcmp(attr->fName, name))
+            return attr->fValue;
+        attr += 1;
+    }
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const
+{
+    return node->fAttrCount ? node->attrs() : NULL;
+}
+
+const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    if (attr == NULL)
+        return NULL;
+    return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL;
+}
+
+const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    SkASSERT(attr);
+    return attr->fName;
+}
+
+const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    SkASSERT(attr);
+    return attr->fValue;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node)
+{
+    SkASSERT(node);
+    fAttr = node->attrs();
+    fStop = fAttr + node->fAttrCount;
+}
+
+const char* SkDOM::AttrIter::next(const char** value)
+{
+    const char* name = NULL;
+
+    if (fAttr < fStop)
+    {
+        name = fAttr->fName;
+        if (value)
+            *value = fAttr->fValue;
+        fAttr += 1;
+    }
+    return name;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+#include "SkTDArray.h"
+
+static char* dupstr(SkChunkAlloc* chunk, const char src[])
+{
+    SkASSERT(chunk && src);
+    size_t  len = strlen(src);
+    char*   dst = (char*)chunk->alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+    memcpy(dst, src, len + 1);
+    return dst;
+}
+
+class SkDOMParser : public SkXMLParser {
+    bool fNeedToFlush;
+public:
+    SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk)
+    {
+        fRoot = NULL;
+        fLevel = 0;
+        fNeedToFlush = true;
+    }
+    SkDOM::Node* getRoot() const { return fRoot; }
+    SkXMLParserError fParserError;
+protected:
+    void flushAttributes()
+    {
+        int attrCount = fAttrs.count();
+
+        SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr),
+                                                        SkChunkAlloc::kThrow_AllocFailType);
+
+        node->fName = fElemName;
+        node->fFirstChild = NULL;
+        node->fAttrCount = SkToU16(attrCount);
+        node->fType = SkDOM::kElement_Type;
+
+        if (fRoot == NULL)
+        {
+            node->fNextSibling = NULL;
+            fRoot = node;
+        }
+        else    // this adds siblings in reverse order. gets corrected in onEndElement()
+        {
+            SkDOM::Node* parent = fParentStack.top();
+            SkASSERT(fRoot && parent);
+            node->fNextSibling = parent->fFirstChild;
+            parent->fFirstChild = node;
+        }
+        *fParentStack.push() = node;
+
+        memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
+        fAttrs.reset();
+
+    }
+    virtual bool onStartElement(const char elem[])
+    {
+        if (fLevel > 0 && fNeedToFlush)
+            this->flushAttributes();
+        fNeedToFlush = true;
+        fElemName = dupstr(fAlloc, elem);
+        ++fLevel;
+        return false;
+    }
+    virtual bool onAddAttribute(const char name[], const char value[])
+    {
+        SkDOM::Attr* attr = fAttrs.append();
+        attr->fName = dupstr(fAlloc, name);
+        attr->fValue = dupstr(fAlloc, value);
+        return false;
+    }
+    virtual bool onEndElement(const char elem[])
+    {
+        --fLevel;
+        if (fNeedToFlush)
+            this->flushAttributes();
+        fNeedToFlush = false;
+
+        SkDOM::Node* parent;
+
+        fParentStack.pop(&parent);
+
+        SkDOM::Node* child = parent->fFirstChild;
+        SkDOM::Node* prev = NULL;
+        while (child)
+        {
+            SkDOM::Node* next = child->fNextSibling;
+            child->fNextSibling = prev;
+            prev = child;
+            child = next;
+        }
+        parent->fFirstChild = prev;
+        return false;
+    }
+private:
+    SkTDArray<SkDOM::Node*> fParentStack;
+    SkChunkAlloc*   fAlloc;
+    SkDOM::Node*    fRoot;
+
+    // state needed for flushAttributes()
+    SkTDArray<SkDOM::Attr>  fAttrs;
+    char*                   fElemName;
+    int                     fLevel;
+};
+
+const SkDOM::Node* SkDOM::build(const char doc[], size_t len)
+{
+    fAlloc.reset();
+    SkDOMParser parser(&fAlloc);
+    if (!parser.parse(doc, len))
+    {
+        SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
+        fRoot = NULL;
+        fAlloc.reset();
+        return NULL;
+    }
+    fRoot = parser.getRoot();
+    return fRoot;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser)
+{
+    const char* elem = dom.getName(node);
+
+    parser->startElement(elem);
+    
+    SkDOM::AttrIter iter(dom, node);
+    const char*     name;
+    const char*     value;
+    while ((name = iter.next(&value)) != NULL)
+        parser->addAttribute(name, value);
+
+    node = dom.getFirstChild(node, NULL);
+    while (node)
+    {
+        walk_dom(dom, node, parser);
+        node = dom.getNextSibling(node, NULL);
+    }
+
+    parser->endElement(elem);
+}
+
+const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node)
+{
+    fAlloc.reset();
+    SkDOMParser parser(&fAlloc);
+
+    walk_dom(dom, node, &parser);
+
+    fRoot = parser.getRoot();
+    return fRoot;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+int SkDOM::countChildren(const Node* node, const char elem[]) const
+{
+    int count = 0;
+
+    node = this->getFirstChild(node, elem);
+    while (node)
+    {
+        count += 1;
+        node = this->getNextSibling(node, elem);
+    }
+    return count;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkParse.h"
+
+bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindS32(vstr, value);
+}
+
+bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindScalars(vstr, value, count);
+}
+
+bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindHex(vstr, value);
+}
+
+bool SkDOM::findBool(const Node* node, const char name[], bool* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindBool(vstr, value);
+}
+
+int SkDOM::findList(const Node* node, const char name[], const char list[]) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr ? SkParse::FindList(vstr, list) : -1;
+}
+
+bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && !strcmp(vstr, value);
+}
+
+bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    int32_t     value;
+    return vstr && SkParse::FindS32(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    SkScalar    value;
+    return vstr && SkParse::FindScalar(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    uint32_t    value;
+    return vstr && SkParse::FindHex(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasBool(const Node* node, const char name[], bool target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    bool        value;
+    return vstr && SkParse::FindBool(vstr, &value) && value == target;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static void tab(int level)
+{
+    while (--level >= 0)
+        SkDebugf("\t");
+}
+
+void SkDOM::dump(const Node* node, int level) const
+{
+    if (node == NULL)
+        node = this->getRootNode();
+    if (node)
+    {
+        tab(level);
+        SkDebugf("<%s", this->getName(node));
+
+        const Attr* attr = node->attrs();
+        const Attr* stop = attr + node->fAttrCount;
+        for (; attr < stop; attr++)
+            SkDebugf(" %s=\"%s\"", attr->fName, attr->fValue);
+
+        const Node* child = this->getFirstChild(node);
+        if (child)
+        {
+            SkDebugf(">\n");
+            while (child)
+            {
+                this->dump(child, level+1);
+                child = this->getNextSibling(child);
+            }
+            tab(level);
+            SkDebugf("</%s>\n", node->fName);
+        }
+        else
+            SkDebugf("/>\n");
+    }
+}
+
+void SkDOM::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    static const char gDoc[] = 
+        "<root a='1' b='2'>"
+            "<elem1 c='3' />"
+            "<elem2 d='4' />"
+            "<elem3 e='5'>"
+                "<subelem1/>"
+                "<subelem2 f='6' g='7'/>"
+            "</elem3>"
+            "<elem4 h='8'/>"
+        "</root>"
+        ;
+
+    SkDOM   dom;
+
+    SkASSERT(dom.getRootNode() == NULL);
+
+    const Node* root = dom.build(gDoc, sizeof(gDoc) - 1);
+    SkASSERT(root && dom.getRootNode() == root);
+
+    const char* v = dom.findAttr(root, "a");
+    SkASSERT(v && !strcmp(v, "1"));
+    v = dom.findAttr(root, "b");
+    SkASSERT(v && !strcmp(v, "2"));
+    v = dom.findAttr(root, "c");
+    SkASSERT(v == NULL);
+
+    SkASSERT(dom.getFirstChild(root, "elem1"));
+    SkASSERT(!dom.getFirstChild(root, "subelem1"));
+
+    dom.dump();
+#endif
+}
+
+#endif
+
diff --git a/legacy/src/xml/SkJS.cpp b/legacy/src/xml/SkJS.cpp
new file mode 100644
index 0000000..f2e7a83
--- /dev/null
+++ b/legacy/src/xml/SkJS.cpp
@@ -0,0 +1,229 @@
+
+/*
+ * 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 <jsapi.h>
+
+#include "SkJS.h"
+#include "SkString.h"
+
+#ifdef _WIN32_WCE
+extern "C" {
+    void abort() {
+        SkASSERT(0);
+    }
+
+    unsigned int _control87(unsigned int _new, unsigned int mask ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    time_t mktime(struct tm *timeptr ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+//  int errno;
+
+    char *strdup(const char *) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    char *strerror(int errnum) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    int isatty(void* fd) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    int putenv(const char *envstring) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    char *getenv(const char *varname) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    void GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) {
+        SkASSERT(0);
+    }
+
+    struct tm * localtime(const time_t *timer) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    size_t strftime(char *strDest, size_t maxsize, const char *format,
+        const struct tm *timeptr ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+}
+#endif
+
+static JSBool
+global_enumerate(JSContext *cx, JSObject *obj)
+{
+#ifdef LAZY_STANDARD_CLASSES
+    return JS_EnumerateStandardClasses(cx, obj);
+#else
+    return JS_TRUE;
+#endif
+}
+
+static JSBool
+global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp)
+{
+#ifdef LAZY_STANDARD_CLASSES
+    if ((flags & JSRESOLVE_ASSIGNING) == 0) {
+        JSBool resolved;
+
+        if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
+            return JS_FALSE;
+        if (resolved) {
+            *objp = obj;
+            return JS_TRUE;
+        }
+    }
+#endif
+
+#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
+    if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
+        /*
+         * Do this expensive hack only for unoptimized Unix builds, which are
+         * not used for benchmarking.
+         */
+        char *path, *comp, *full;
+        const char *name;
+        JSBool ok, found;
+        JSFunction *fun;
+
+        if (!JSVAL_IS_STRING(id))
+            return JS_TRUE;
+        path = getenv("PATH");
+        if (!path)
+            return JS_TRUE;
+        path = JS_strdup(cx, path);
+        if (!path)
+            return JS_FALSE;
+        name = JS_GetStringBytes(JSVAL_TO_STRING(id));
+        ok = JS_TRUE;
+        for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
+            if (*comp != '\0') {
+                full = JS_smprintf("%s/%s", comp, name);
+                if (!full) {
+                    JS_ReportOutOfMemory(cx);
+                    ok = JS_FALSE;
+                    break;
+                }
+            } else {
+                full = (char *)name;
+            }
+            found = (access(full, X_OK) == 0);
+            if (*comp != '\0')
+                free(full);
+            if (found) {
+                fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE);
+                ok = (fun != NULL);
+                if (ok)
+                    *objp = obj;
+                break;
+            }
+        }
+        JS_free(cx, path);
+        return ok;
+    }
+#else
+    return JS_TRUE;
+#endif
+}
+
+JSClass global_class = {
+    "global", JSCLASS_NEW_RESOLVE,
+    JS_PropertyStub,  JS_PropertyStub,
+    JS_PropertyStub,  JS_PropertyStub,
+    global_enumerate, (JSResolveOp) global_resolve,
+    JS_ConvertStub,   JS_FinalizeStub
+};
+
+SkJS::SkJS(void* hwnd) : SkOSWindow(hwnd) {
+    if ((fRuntime = JS_NewRuntime(0x100000)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    if ((fContext = JS_NewContext(fRuntime, 0x1000)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    ;
+    if ((fGlobal = JS_NewObject(fContext, &global_class, NULL, NULL)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    if (JS_InitStandardClasses(fContext, fGlobal) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    setConfig(SkBitmap::kARGB32_Config);
+    updateSize();
+    setVisibleP(true);
+    InitializeDisplayables(getBitmap(), fContext, fGlobal, NULL);
+}
+
+SkJS::~SkJS() {
+    DisposeDisplayables();
+    JS_DestroyContext(fContext);
+    JS_DestroyRuntime(fRuntime);
+    JS_ShutDown();
+}
+
+SkBool SkJS::EvaluateScript(const char* script, jsval* rVal) {
+    return JS_EvaluateScript(fContext, fGlobal, script, strlen(script),
+        "memory" /* no file name */, 0 /* no line number */, rVal);
+}
+
+SkBool SkJS::ValueToString(jsval value, SkString* string) {
+     JSString* str = JS_ValueToString(fContext, value);
+     if (str == NULL)
+         return false;
+     string->set(JS_GetStringBytes(str));
+     return true;
+}
+
+#ifdef SK_DEBUG
+void SkJS::Test(void* hwnd) {
+    SkJS js(hwnd);
+    jsval val;
+    SkBool success = js.EvaluateScript("22/7", &val);
+    SkASSERT(success);
+    SkString string;
+    success = js.ValueToString(val, &string);
+    SkASSERT(success);
+    SkASSERT(strcmp(string.c_str(), "3.142857142857143") == 0);
+    success = js.EvaluateScript(
+        "var rect = new rectangle();"
+        "rect.left = 4;"
+        "rect.top = 10;"
+        "rect.right = 20;"
+        "rect.bottom = 30;"
+        "rect.width = rect.height + 20;"
+        "rect.draw();"
+        , &val);
+    SkASSERT(success);
+    success = js.ValueToString(val, &string);
+    SkASSERT(success);
+}
+#endifASSERT(success);
+
diff --git a/legacy/src/xml/SkJSDisplayable.cpp b/legacy/src/xml/SkJSDisplayable.cpp
new file mode 100644
index 0000000..0e14fde
--- /dev/null
+++ b/legacy/src/xml/SkJSDisplayable.cpp
@@ -0,0 +1,460 @@
+
+/*
+ * 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 <jsapi.h>
+#include "SkJS.h"
+#include "SkDisplayType.h"
+//#include "SkAnimateColor.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+//#include "SkAnimateTransform.h"
+#include "SkCanvas.h"
+//#include "SkDimensions.h"
+#include "SkDisplayAdd.h"
+#include "SkDisplayApply.h"
+//#include "SkDisplayBefore.h"
+#include "SkDisplayEvent.h"
+//#include "SkDisplayFocus.h"
+#include "SkDisplayInclude.h"
+#include "SkDisplayPost.h"
+#include "SkDisplayRandom.h"
+#include "SkDraw3D.h"
+#include "SkDrawBitmap.h"
+#include "SkDrawClip.h"
+#include "SkDrawDash.h"
+#include "SkDrawDiscrete.h"
+#include "SkDrawEmboss.h"
+//#include "SkDrawFont.h"
+#include "SkDrawFull.h"
+#include "SkDrawGradient.h"
+#include "SkDrawLine.h"
+//#include "SkDrawMaskFilter.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawOval.h"
+#include "SkDrawPaint.h"
+#include "SkDrawPath.h"
+#include "SkDrawPoint.h"
+// #include "SkDrawStroke.h"
+#include "SkDrawText.h"
+#include "SkDrawTo.h"
+//#include "SkDrawTransferMode.h"
+#include "SkDrawTransparentShader.h"
+//#include "SkDrawUse.h"
+#include "SkMatrixParts.h"
+#include "SkPathParts.h"
+#include "SkPostParts.h"
+#include "SkScript.h"
+#include "SkSnapshot.h"
+#include "SkTextOnPath.h"
+#include "SkTextToPath.h"
+
+
+class SkJSDisplayable {
+public:
+    SkJSDisplayable() : fDisplayable(NULL) {}
+    ~SkJSDisplayable() { delete fDisplayable; }
+    static void Destructor(JSContext *cx, JSObject *obj);
+    static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+    static JSBool SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+    static SkCanvas* gCanvas;
+    static SkPaint* gPaint;
+    static JSBool Draw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
+    SkDisplayable* fDisplayable;
+};
+
+SkCanvas* SkJSDisplayable::gCanvas;
+SkPaint* SkJSDisplayable::gPaint;
+
+JSBool SkJSDisplayable::Draw(JSContext *cx, JSObject *obj, uintN argc,
+                                    jsval *argv, jsval *rval)
+{
+    SkJSDisplayable *p = (SkJSDisplayable*) JS_GetPrivate(cx, obj);
+    SkASSERT(p->fDisplayable->isDrawable());
+    SkDrawable* drawable = (SkDrawable*) p->fDisplayable;
+    SkAnimateMaker maker(NULL, gCanvas, gPaint);
+    drawable->draw(maker);
+    return JS_TRUE;
+}
+
+
+JSFunctionSpec SkJSDisplayable_methods[] = 
+{
+    { "draw", SkJSDisplayable::Draw, 1, 0, 0 },
+    { 0 }
+};
+
+static JSPropertySpec* gDisplayableProperties[kNumberOfTypes];
+static JSClass gDisplayableClasses[kNumberOfTypes];
+
+#define JS_INIT(_prefix, _class) \
+static JSBool _class##Constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { \
+    SkJSDisplayable* jsDisplayable = new SkJSDisplayable(); \
+    jsDisplayable->fDisplayable = new _prefix##_class(); \
+    JS_SetPrivate(cx, obj, (void*) jsDisplayable); \
+    return JS_TRUE; \
+} \
+    \
+static JSObject* _class##Init(JSContext *cx, JSObject *obj, JSObject *proto) { \
+    JSObject *newProtoObj = JS_InitClass(cx, obj, proto, &gDisplayableClasses[SkType_##_class], \
+        _class##Constructor, 0, \
+        NULL, SkJSDisplayable_methods , \
+        NULL, NULL); \
+    JS_DefineProperties(cx, newProtoObj, gDisplayableProperties[SkType_##_class]); \
+    return newProtoObj; \
+}
+
+JS_INIT(Sk, Add)
+JS_INIT(Sk, AddCircle)
+JS_INIT(Sk, AddOval)
+JS_INIT(Sk, AddPath)
+JS_INIT(Sk, AddRectangle)
+JS_INIT(Sk, AddRoundRect)
+//JS_INIT(Sk, After)
+JS_INIT(Sk, Apply)
+// JS_INIT(Sk, Animate)
+//JS_INIT(Sk, AnimateColor)
+JS_INIT(Sk, AnimateField)
+//JS_INIT(Sk, AnimateRotate)
+//JS_INIT(Sk, AnimateScale)
+//JS_INIT(Sk, AnimateTranslate)
+JS_INIT(SkDraw, Bitmap)
+JS_INIT(Sk, BaseBitmap)
+//JS_INIT(Sk, Before)
+JS_INIT(SkDraw, BitmapShader)
+JS_INIT(SkDraw, Blur)
+JS_INIT(SkDraw, Clip)
+JS_INIT(SkDraw, Color)
+JS_INIT(Sk, CubicTo)
+JS_INIT(Sk, Dash)
+JS_INIT(Sk, Data)
+//JS_INIT(Sk, Dimensions)
+JS_INIT(Sk, Discrete)
+JS_INIT(Sk, DrawTo)
+JS_INIT(SkDraw, Emboss)
+JS_INIT(SkDisplay, Event)
+// JS_INIT(SkDraw, Font)
+// JS_INIT(Sk, Focus)
+JS_INIT(Sk, Image)
+JS_INIT(Sk, Include)
+// JS_INIT(Sk, Input)
+JS_INIT(Sk, Line)
+JS_INIT(Sk, LinearGradient)
+JS_INIT(Sk, LineTo)
+JS_INIT(SkDraw, Matrix)
+JS_INIT(Sk, Move)
+JS_INIT(Sk, MoveTo)
+JS_INIT(Sk, Oval)
+JS_INIT(SkDraw, Path)
+JS_INIT(SkDraw, Paint)
+JS_INIT(Sk, DrawPoint)
+JS_INIT(Sk, PolyToPoly)
+JS_INIT(Sk, Polygon)
+JS_INIT(Sk, Polyline)
+JS_INIT(Sk, Post)
+JS_INIT(Sk, QuadTo)
+JS_INIT(Sk, RadialGradient)
+JS_INIT(SkDisplay, Random)
+JS_INIT(Sk, RectToRect)
+JS_INIT(Sk, Rectangle)
+JS_INIT(Sk, Remove)
+JS_INIT(Sk, Replace)
+JS_INIT(Sk, Rotate)
+JS_INIT(Sk, RoundRect)
+JS_INIT(Sk, Scale)
+JS_INIT(Sk, Set)
+JS_INIT(Sk, Skew)
+// JS_INIT(Sk, 3D_Camera)
+// JS_INIT(Sk, 3D_Patch)
+JS_INIT(Sk, Snapshot)
+// JS_INIT(SkDraw, Stroke)
+JS_INIT(Sk, Text)
+JS_INIT(Sk, TextOnPath)
+JS_INIT(Sk, TextToPath)
+JS_INIT(Sk, Translate)
+//JS_INIT(Sk, Use)
+
+#if SK_USE_CONDENSED_INFO == 0
+static void GenerateTables() {
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        int infoCount;
+        SkDisplayTypes type = gTypeNames[index].fType;
+        const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, type, &infoCount);
+        if (info == NULL)
+            continue;
+        gDisplayableProperties[type] = new JSPropertySpec[infoCount + 1];
+        JSPropertySpec* propertySpec = gDisplayableProperties[type];
+        memset(propertySpec, 0, sizeof (JSPropertySpec) * (infoCount + 1));
+        for (int inner = 0; inner < infoCount; inner++) {
+            if (info[inner].fType == SkType_BaseClassInfo)
+                continue;
+            propertySpec[inner].name = info[inner].fName;
+            propertySpec[inner].tinyid = inner;
+            propertySpec[inner].flags = JSPROP_ENUMERATE;
+        }
+        gDisplayableClasses[type].name = gTypeNames[index].fName;
+        gDisplayableClasses[type].flags = JSCLASS_HAS_PRIVATE;
+        gDisplayableClasses[type].addProperty = JS_PropertyStub;
+        gDisplayableClasses[type].delProperty = JS_PropertyStub;
+        gDisplayableClasses[type].getProperty = SkJSDisplayable::GetProperty;
+        gDisplayableClasses[type].setProperty = SkJSDisplayable::SetProperty;
+        gDisplayableClasses[type].enumerate = JS_EnumerateStub;
+        gDisplayableClasses[type].resolve = JS_ResolveStub;
+        gDisplayableClasses[type].convert = JS_ConvertStub;
+        gDisplayableClasses[type].finalize = SkJSDisplayable::Destructor;
+    }
+}
+#endif
+
+void SkJSDisplayable::Destructor(JSContext *cx, JSObject *obj) {
+    delete (SkJSDisplayable*) JS_GetPrivate(cx, obj);
+}
+
+JSBool SkJSDisplayable::GetProperty(JSContext *cx, JSObject *obj, jsval id,
+                                 jsval *vp)
+{
+    if (JSVAL_IS_INT(id) == 0)
+        return JS_TRUE; 
+    SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
+    SkDisplayable* displayable = p->fDisplayable;
+    SkDisplayTypes displayableType = displayable->getType();
+    int members;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members);
+    int idIndex = JSVAL_TO_INT(id);
+    SkASSERT(idIndex >= 0 && idIndex < members);
+    info = &info[idIndex];
+    SkDisplayTypes infoType = (SkDisplayTypes) info->fType;
+    SkScalar scalar = 0;
+    S32 s32 = 0;
+    SkString* string= NULL;
+    JSString *str;
+    if (infoType == SkType_MemberProperty) {
+        infoType = info->propertyType();
+        switch (infoType) {
+            case SkType_Scalar: {
+                SkScriptValue scriptValue;
+                bool success = displayable->getProperty(info->propertyIndex(), &scriptValue);
+                SkASSERT(scriptValue.fType == SkType_Scalar);
+                scalar = scriptValue.fOperand.fScalar;
+                } break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    } else {
+        SkASSERT(info->fCount == 1);
+        switch (infoType) {
+            case SkType_Boolean:
+            case SkType_Color:
+            case SkType_S32:
+                s32 = *(S32*) info->memberData(displayable);
+                break;
+            case SkType_String:
+                info->getString(displayable, &string);
+                break;
+            case SkType_Scalar:
+                SkOperand operand;
+                info->getValue(displayable, &operand, 1);
+                scalar = operand.fScalar;
+                break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    }
+    switch (infoType) {
+        case SkType_Boolean:
+            *vp = BOOLEAN_TO_JSVAL(s32);
+            break;
+        case SkType_Color:
+        case SkType_S32:
+            *vp = INT_TO_JSVAL(s32);
+            break;
+        case SkType_Scalar:
+            if (SkScalarFraction(scalar) == 0)
+                *vp = INT_TO_JSVAL(SkScalarFloor(scalar));
+            else
+#ifdef SK_SCALAR_IS_FLOAT
+            *vp = DOUBLE_TO_JSVAL(scalar);
+#else
+            *vp = DOUBLE_TO_JSVAL(scalar / 65536.0f );
+#endif
+            break;
+        case SkType_String:
+            str = JS_NewStringCopyN(cx, string->c_str(), string->size());
+            *vp = STRING_TO_JSVAL(str);
+            break;
+        default:
+            SkASSERT(0); // !!! unimplemented
+    }
+    return JS_TRUE;
+}
+
+JSBool SkJSDisplayable::SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
+    if (JSVAL_IS_INT(id) == 0)
+        return JS_TRUE; 
+    SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
+    SkDisplayable* displayable = p->fDisplayable;
+    SkDisplayTypes displayableType = displayable->getType();
+    int members;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members);
+    int idIndex = JSVAL_TO_INT(id);
+    SkASSERT(idIndex >= 0 && idIndex < members);
+    info = &info[idIndex];
+    SkDisplayTypes infoType = info->getType();
+    SkScalar scalar = 0;
+    S32 s32 = 0;
+    SkString string;
+    JSString* str;
+    jsval value = *vp;
+    switch (infoType) {
+        case SkType_Boolean:
+            s32 = JSVAL_TO_BOOLEAN(value);
+            break;
+        case SkType_Color:
+        case SkType_S32:
+            s32 = JSVAL_TO_INT(value);
+            break;
+        case SkType_Scalar:
+            if (JSVAL_IS_INT(value))
+                scalar = SkIntToScalar(JSVAL_TO_INT(value));
+            else {
+                SkASSERT(JSVAL_IS_DOUBLE(value));
+#ifdef SK_SCALAR_IS_FLOAT
+                scalar = (float) *(double*) JSVAL_TO_DOUBLE(value);
+#else
+                scalar = (SkFixed)  (*(double*)JSVAL_TO_DOUBLE(value) * 65536.0);
+#endif
+            }
+            break;
+        case SkType_String:
+            str = JS_ValueToString(cx, value);
+            string.set(JS_GetStringBytes(str));
+            break;
+        default:
+            SkASSERT(0); // !!! unimplemented
+    }
+    if (info->fType == SkType_MemberProperty) {
+        switch (infoType) {
+            case SkType_Scalar: {
+                SkScriptValue scriptValue;
+                scriptValue.fType = SkType_Scalar;
+                scriptValue.fOperand.fScalar = scalar;
+                displayable->setProperty(-1 - (int) info->fOffset, scriptValue);
+                } break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    } else {
+        SkASSERT(info->fCount == 1);
+        switch (infoType) {
+            case SkType_Boolean:
+            case SkType_Color:
+            case SkType_S32:
+                s32 = *(S32*) ((const char*) displayable + info->fOffset);
+                break;
+            case SkType_String:
+                info->setString(displayable, &string);
+                break;
+            case SkType_Scalar:
+                SkOperand operand;
+                operand.fScalar = scalar;
+                info->setValue(displayable, &operand, 1);
+                break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    }
+    return JS_TRUE;
+}
+
+void SkJS::InitializeDisplayables(const SkBitmap& bitmap, JSContext *cx, JSObject *obj, JSObject *proto) {
+    SkJSDisplayable::gCanvas = new SkCanvas(bitmap);
+    SkJSDisplayable::gPaint = new SkPaint();
+#if SK_USE_CONDENSED_INFO == 0
+    GenerateTables();
+#else
+    SkASSERT(0); // !!! compressed version hasn't been implemented
+#endif
+    AddInit(cx, obj, proto);
+    AddCircleInit(cx, obj, proto);
+    AddOvalInit(cx, obj, proto);
+    AddPathInit(cx, obj, proto);
+    AddRectangleInit(cx, obj, proto);
+    AddRoundRectInit(cx, obj, proto);
+//  AfterInit(cx, obj, proto);
+    ApplyInit(cx, obj, proto);
+    // AnimateInit(cx, obj, proto);
+//  AnimateColorInit(cx, obj, proto);
+    AnimateFieldInit(cx, obj, proto);
+//  AnimateRotateInit(cx, obj, proto);
+//  AnimateScaleInit(cx, obj, proto);
+//  AnimateTranslateInit(cx, obj, proto);
+    BitmapInit(cx, obj, proto);
+//  BaseBitmapInit(cx, obj, proto);
+//  BeforeInit(cx, obj, proto);
+    BitmapShaderInit(cx, obj, proto);
+    BlurInit(cx, obj, proto);
+    ClipInit(cx, obj, proto);
+    ColorInit(cx, obj, proto);
+    CubicToInit(cx, obj, proto);
+    DashInit(cx, obj, proto);
+    DataInit(cx, obj, proto);
+//  DimensionsInit(cx, obj, proto);
+    DiscreteInit(cx, obj, proto);
+    DrawToInit(cx, obj, proto);
+    EmbossInit(cx, obj, proto);
+    EventInit(cx, obj, proto);
+//  FontInit(cx, obj, proto);
+//  FocusInit(cx, obj, proto);
+    ImageInit(cx, obj, proto);
+    IncludeInit(cx, obj, proto);
+//  InputInit(cx, obj, proto);
+    LineInit(cx, obj, proto);
+    LinearGradientInit(cx, obj, proto);
+    LineToInit(cx, obj, proto);
+    MatrixInit(cx, obj, proto);
+    MoveInit(cx, obj, proto);
+    MoveToInit(cx, obj, proto);
+    OvalInit(cx, obj, proto);
+    PathInit(cx, obj, proto);
+    PaintInit(cx, obj, proto);
+    DrawPointInit(cx, obj, proto);
+    PolyToPolyInit(cx, obj, proto);
+    PolygonInit(cx, obj, proto);
+    PolylineInit(cx, obj, proto);
+    PostInit(cx, obj, proto);
+    QuadToInit(cx, obj, proto);
+    RadialGradientInit(cx, obj, proto);
+    RandomInit(cx, obj, proto);
+    RectToRectInit(cx, obj, proto);
+    RectangleInit(cx, obj, proto);
+    RemoveInit(cx, obj, proto);
+    ReplaceInit(cx, obj, proto);
+    RotateInit(cx, obj, proto);
+    RoundRectInit(cx, obj, proto);
+    ScaleInit(cx, obj, proto);
+    SetInit(cx, obj, proto);
+    SkewInit(cx, obj, proto);
+    // 3D_CameraInit(cx, obj, proto);
+    // 3D_PatchInit(cx, obj, proto);
+    SnapshotInit(cx, obj, proto);
+//  StrokeInit(cx, obj, proto);
+    TextInit(cx, obj, proto);
+    TextOnPathInit(cx, obj, proto);
+    TextToPathInit(cx, obj, proto);
+    TranslateInit(cx, obj, proto);
+//  UseInit(cx, obj, proto);
+}
+
+void SkJS::DisposeDisplayables() {
+    delete SkJSDisplayable::gPaint;
+    delete SkJSDisplayable::gCanvas;
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        SkDisplayTypes type = gTypeNames[index].fType;
+        delete[] gDisplayableProperties[type];
+    }
+}
diff --git a/legacy/src/xml/SkXMLParser.cpp b/legacy/src/xml/SkXMLParser.cpp
new file mode 100644
index 0000000..17329be
--- /dev/null
+++ b/legacy/src/xml/SkXMLParser.cpp
@@ -0,0 +1,87 @@
+
+/*
+ * 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 "SkXMLParser.h"
+
+static char const* const gErrorStrings[] = {
+    "empty or missing file ",
+    "unknown element ",
+    "unknown attribute name ",
+    "error in attribute value ",
+    "duplicate ID ",
+    "unknown error "
+};
+
+SkXMLParserError::SkXMLParserError() : fCode(kNoError), fLineNumber(-1),
+    fNativeCode(-1)
+{
+    reset();
+}
+
+SkXMLParserError::~SkXMLParserError()
+{
+    // need a virtual destructor for our subclasses
+}
+
+void SkXMLParserError::getErrorString(SkString* str) const
+{
+    SkASSERT(str);
+    SkString temp;
+    if (fCode != kNoError) {
+        if ((unsigned)fCode < SK_ARRAY_COUNT(gErrorStrings))
+            temp.set(gErrorStrings[fCode - 1]);
+        temp.append(fNoun);
+    } else
+        SkXMLParser::GetNativeErrorString(fNativeCode, &temp);
+    str->append(temp);
+}
+
+void SkXMLParserError::reset() {
+    fCode = kNoError;
+    fLineNumber = -1;
+    fNativeCode = -1;
+}
+
+
+////////////////
+
+SkXMLParser::SkXMLParser(SkXMLParserError* parserError) : fParser(NULL), fError(parserError)
+{
+}
+
+SkXMLParser::~SkXMLParser()
+{
+}
+
+bool SkXMLParser::startElement(const char elem[])
+{
+    return this->onStartElement(elem);
+}
+
+bool SkXMLParser::addAttribute(const char name[], const char value[])
+{
+    return this->onAddAttribute(name, value);
+}
+
+bool SkXMLParser::endElement(const char elem[])
+{
+    return this->onEndElement(elem);
+}
+
+bool SkXMLParser::text(const char text[], int len) 
+{
+    return this->onText(text, len);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SkXMLParser::onStartElement(const char elem[]) {return false; }
+bool SkXMLParser::onAddAttribute(const char name[], const char value[]) {return false; }
+bool SkXMLParser::onEndElement(const char elem[]) { return false; }
+bool SkXMLParser::onText(const char text[], int len) {return false; }
diff --git a/legacy/src/xml/SkXMLPullParser.cpp b/legacy/src/xml/SkXMLPullParser.cpp
new file mode 100644
index 0000000..03fed42
--- /dev/null
+++ b/legacy/src/xml/SkXMLPullParser.cpp
@@ -0,0 +1,139 @@
+
+/*
+ * 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 "SkXMLParser.h"
+#include "SkStream.h"
+
+static void reset(SkXMLPullParser::Curr* curr)
+{
+    curr->fEventType = SkXMLPullParser::ERROR;
+    curr->fName = "";
+    curr->fAttrInfoCount = 0;
+    curr->fIsWhitespace = false;
+}
+
+SkXMLPullParser::SkXMLPullParser() : fStream(NULL)
+{
+    fCurr.fEventType = ERROR;
+    fDepth = -1;
+}
+
+SkXMLPullParser::SkXMLPullParser(SkStream* stream) : fStream(NULL)
+{
+    fCurr.fEventType = ERROR;
+    fDepth = 0;
+    
+    this->setStream(stream);
+}
+
+SkXMLPullParser::~SkXMLPullParser()
+{
+    this->setStream(NULL);
+}
+
+SkStream* SkXMLPullParser::setStream(SkStream* stream)
+{
+    if (fStream && !stream)
+        this->onExit();
+
+    SkRefCnt_SafeAssign(fStream, stream);
+
+    if (fStream)
+    {
+        fCurr.fEventType = START_DOCUMENT;
+        this->onInit();
+    }
+    else
+    {
+        fCurr.fEventType = ERROR;
+    }
+    fDepth = 0;
+
+    return fStream;
+}
+
+SkXMLPullParser::EventType SkXMLPullParser::nextToken()
+{
+    switch (fCurr.fEventType) {
+    case ERROR:
+    case END_DOCUMENT:
+        break;
+    case END_TAG:
+        fDepth -= 1;
+        // fall through
+    default:        
+        reset(&fCurr);
+        fCurr.fEventType = this->onNextToken();
+        break;
+    }
+    
+    switch (fCurr.fEventType) {
+    case START_TAG:
+        fDepth += 1;
+        break;
+    default:
+        break;
+    }
+
+    return fCurr.fEventType;
+}
+
+const char* SkXMLPullParser::getName()
+{
+    switch (fCurr.fEventType) {
+    case START_TAG:
+    case END_TAG:
+        return fCurr.fName;
+    default:
+        return NULL;
+    }
+}
+
+const char* SkXMLPullParser::getText()
+{
+    switch (fCurr.fEventType) {
+    case TEXT:
+    case IGNORABLE_WHITESPACE:
+        return fCurr.fName;
+    default:
+        return NULL;
+    }
+}
+
+bool SkXMLPullParser::isWhitespace()
+{
+    switch (fCurr.fEventType) {
+    case IGNORABLE_WHITESPACE:
+        return true;
+    case TEXT:
+    case CDSECT:
+        return fCurr.fIsWhitespace;
+    default:
+        return false;   // unknown/illegal
+    }
+}
+
+int SkXMLPullParser::getAttributeCount()
+{
+    return fCurr.fAttrInfoCount;
+}
+
+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/legacy/src/xml/SkXMLWriter.cpp b/legacy/src/xml/SkXMLWriter.cpp
new file mode 100644
index 0000000..935745d
--- /dev/null
+++ b/legacy/src/xml/SkXMLWriter.cpp
@@ -0,0 +1,334 @@
+
+/*
+ * 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 "SkXMLWriter.h"
+#include "SkStream.h"
+
+SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup)
+{
+}
+
+SkXMLWriter::~SkXMLWriter()
+{
+    SkASSERT(fElems.count() == 0);
+}
+
+void SkXMLWriter::flush()
+{
+    while (fElems.count())
+        this->endElement();
+}
+
+void SkXMLWriter::addAttribute(const char name[], const char value[])
+{
+    this->addAttributeLen(name, value, strlen(value));
+}
+
+void SkXMLWriter::addS32Attribute(const char name[], int32_t value)
+{
+    SkString    tmp;
+    tmp.appendS32(value);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::addHexAttribute(const char name[], uint32_t value, int minDigits)
+{
+    SkString    tmp("0x");
+    tmp.appendHex(value, minDigits);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value)
+{
+    SkString    tmp;
+    tmp.appendScalar(value);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::doEnd(Elem* elem)
+{
+    delete elem;
+}
+
+bool SkXMLWriter::doStart(const char name[], size_t length)
+{
+    int level = fElems.count();
+    bool firstChild = level > 0 && !fElems[level-1]->fHasChildren;
+    if (firstChild)
+        fElems[level-1]->fHasChildren = true;
+    Elem** elem = fElems.push();
+    *elem = new Elem;
+    (*elem)->fName.set(name, length);
+    (*elem)->fHasChildren = 0;
+    return firstChild;
+}
+
+SkXMLWriter::Elem* SkXMLWriter::getEnd() 
+{
+    Elem* elem;
+    fElems.pop(&elem);
+    return elem;
+}
+
+const char* SkXMLWriter::getHeader()
+{
+    static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
+    return gHeader;
+}
+
+void SkXMLWriter::startElement(const char name[])
+{
+    this->startElementLen(name, strlen(name));
+}
+
+static const char* escape_char(char c, char storage[2])
+{
+    static const char* gEscapeChars[] = {
+        "<&lt;",
+        ">&gt;",
+        //"\"&quot;",
+        //"'&apos;",
+        "&&amp;"
+    };
+
+    const char** array = gEscapeChars;
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gEscapeChars); i++)
+    {
+        if (array[i][0] == c)
+            return &array[i][1];
+    }
+    storage[0] = c;
+    storage[1] = 0;
+    return storage;
+}
+
+static size_t escape_markup(char dst[], const char src[], size_t length)
+{
+    size_t      extra = 0;
+    const char* stop = src + length;
+
+    while (src < stop)
+    {
+        char        orig[2];
+        const char* seq = escape_char(*src, orig);
+        size_t      seqSize = strlen(seq);
+
+        if (dst)
+        {
+            memcpy(dst, seq, seqSize);
+            dst += seqSize;
+        }
+
+        // now record the extra size needed
+        extra += seqSize - 1;   // minus one to subtract the original char
+
+        // bump to the next src char
+        src += 1;
+    }
+    return extra;
+}
+
+void SkXMLWriter::addAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkString valueStr;
+
+    if (fDoEscapeMarkup)
+    {
+        size_t   extra = escape_markup(NULL, value, length);
+        if (extra)
+        {
+            valueStr.resize(length + extra);
+            (void)escape_markup(valueStr.writable_str(), value, length);
+            value = valueStr.c_str();
+            length += extra;
+        }
+    }
+    this->onAddAttributeLen(name, value, length);
+}
+
+void SkXMLWriter::startElementLen(const char elem[], size_t length)
+{
+    this->onStartElementLen(elem, length);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static void write_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLWriter* w, bool skipRoot)
+{
+    if (!skipRoot)
+    {
+        w->startElement(dom.getName(node));
+
+        SkDOM::AttrIter iter(dom, node);
+        const char* name;
+        const char* value;
+        while ((name = iter.next(&value)) != NULL)
+            w->addAttribute(name, value);
+    }
+
+    node = dom.getFirstChild(node, NULL);
+    while (node)
+    {
+        write_dom(dom, node, w, false);
+        node = dom.getNextSibling(node, NULL);
+    }
+
+    if (!skipRoot)
+        w->endElement();
+}
+
+void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot)
+{
+    if (node)
+        write_dom(dom, node, this, skipRoot);
+}
+
+void SkXMLWriter::writeHeader()
+{
+}
+
+// SkXMLStreamWriter
+
+static void tab(SkWStream& stream, int level)
+{
+    for (int i = 0; i < level; i++)
+        stream.writeText("\t");
+}
+
+SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream)
+{
+}
+
+SkXMLStreamWriter::~SkXMLStreamWriter()
+{
+    this->flush();
+}
+
+void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkASSERT(!fElems.top()->fHasChildren);
+    fStream.writeText(" ");
+    fStream.writeText(name);
+    fStream.writeText("=\"");
+    fStream.write(value, length);
+    fStream.writeText("\"");
+}
+
+void SkXMLStreamWriter::onEndElement()
+{
+    Elem* elem = getEnd();
+    if (elem->fHasChildren)
+    {
+        tab(fStream, fElems.count());
+        fStream.writeText("</");
+        fStream.writeText(elem->fName.c_str());
+        fStream.writeText(">");
+    }
+    else
+        fStream.writeText("/>");
+    fStream.newline();
+    doEnd(elem);
+}
+
+void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length)
+{
+    int level = fElems.count();
+    if (this->doStart(name, length))
+    {
+        // the first child, need to close with >
+        fStream.writeText(">");
+        fStream.newline();
+    }
+
+    tab(fStream, level);
+    fStream.writeText("<");
+    fStream.write(name, length);
+}
+
+void SkXMLStreamWriter::writeHeader()
+{
+    const char* header = getHeader();
+    fStream.write(header, strlen(header));
+    fStream.newline();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+
+SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser)
+    : SkXMLWriter(false), fParser(*parser)
+{
+}
+
+SkXMLParserWriter::~SkXMLParserWriter()
+{
+    this->flush();
+}
+
+void SkXMLParserWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkASSERT(fElems.count() == 0 || !fElems.top()->fHasChildren);
+    SkString str(value, length);
+    fParser.addAttribute(name, str.c_str());
+}
+
+void SkXMLParserWriter::onEndElement()
+{
+    Elem* elem = this->getEnd();
+    fParser.endElement(elem->fName.c_str());
+    this->doEnd(elem);
+}
+
+void SkXMLParserWriter::onStartElementLen(const char name[], size_t length)
+{
+    (void)this->doStart(name, length);
+    SkString str(name, length);
+    fParser.startElement(str.c_str());
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkXMLStreamWriter::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkDebugWStream  s;
+    SkXMLStreamWriter       w(&s);
+
+    w.startElement("elem0");
+    w.addAttribute("hello", "world");
+    w.addS32Attribute("dec", 42);
+    w.addHexAttribute("hex", 0x42, 3);
+#ifdef SK_SCALAR_IS_FLOAT
+    w.addScalarAttribute("scalar", -4.2f);
+#endif
+    w.startElement("elem1");
+        w.endElement();
+        w.startElement("elem1");
+        w.addAttribute("name", "value");
+        w.endElement();
+        w.startElement("elem1");
+            w.startElement("elem2");
+                w.startElement("elem3");
+                w.addAttribute("name", "value");
+                w.endElement();
+            w.endElement();
+            w.startElement("elem2");
+            w.endElement();
+        w.endElement();
+    w.endElement();
+#endif
+}
+
+#endif
+
diff --git a/legacy/src/xml/xml_files.mk b/legacy/src/xml/xml_files.mk
new file mode 100644
index 0000000..d4342dd
--- /dev/null
+++ b/legacy/src/xml/xml_files.mk
@@ -0,0 +1,3 @@
+SOURCE := \
+    SkDOM.cpp \
+    SkXMLParser.cpp
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_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/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/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/SkLanguage.cpp b/src/core/SkLanguage.cpp
index 3b8ba3c..2d0bcd5 100644
--- a/src/core/SkLanguage.cpp
+++ b/src/core/SkLanguage.cpp
@@ -16,7 +16,6 @@
 
 SkLanguage SkLanguage::getParent() const {
     SkASSERT(fInfo != NULL);
-    SkASSERT(fInfo->fTag != NULL);
     const char* tag = fInfo->fTag.c_str();
     SkASSERT(tag != NULL);
 
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
index e69de29..53cf430 100644
--- a/src/core/SkMathPriv.h
+++ 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
index 15fb0f9..b3fde17 100644
--- a/src/core/SkOrderedReadBuffer.h
+++ b/src/core/SkOrderedReadBuffer.h
@@ -1,3 +1,4 @@
+
 /*
  * Copyright 2011 Google Inc.
  *
@@ -8,13 +9,119 @@
 #ifndef SkOrderedReadBuffer_DEFINED
 #define SkOrderedReadBuffer_DEFINED
 
-class SkFlattenableReadBuffer {
-public:
-};
+#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(const void*, size_t) {}
+    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
index 83e8e4a..cd37f47 100644
--- a/src/core/SkOrderedWriteBuffer.h
+++ b/src/core/SkOrderedWriteBuffer.h
@@ -1,3 +1,4 @@
+
 /*
  * Copyright 2011 Google Inc.
  *
@@ -8,18 +9,101 @@
 #ifndef SkOrderedWriteBuffer_DEFINED
 #define SkOrderedWriteBuffer_DEFINED
 
-class SkFlattenableWriteBuffer {
-public:
-    enum { kCrossProcess_Flag };
-};
+#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) {}
-    virtual ~SkOrderedWriteBuffer() {}
-    uint32_t size() { return 0; }
-    virtual bool writeToStream(void*) { return false; }
-    void setFlags(uint32_t) {}
+    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 35b5b38..d2d1b7f 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;
@@ -163,11 +175,15 @@
 uint32_t SkPaint::getGenerationID() const {
     return fGenerationID;
 }
+
+void SkPaint::setGenerationID(uint32_t generationID) {
+    fGenerationID = generationID;
+}
 #endif
 
 #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 +367,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 +432,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"
@@ -419,9 +452,9 @@
 }
 
 #ifdef SK_BUILD_FOR_ANDROID
-const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text) {
+const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text, const SkMatrix* deviceMatrix) {
     SkGlyphCache* cache;
-    descriptorProc(NULL, DetachDescProc, &cache, true);
+    descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
 
     const SkGlyph& glyph = cache->getUnicharMetrics(text);
 
@@ -429,9 +462,9 @@
     return glyph;
 }
 
-const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId) {
+const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId, const SkMatrix* deviceMatrix) {
     SkGlyphCache* cache;
-    descriptorProc(NULL, DetachDescProc, &cache, true);
+    descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
 
     const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
 
@@ -439,47 +472,16 @@
     return glyph;
 }
 
-const void* SkPaint::findImage(const SkGlyph& glyph) {
+const void* SkPaint::findImage(const SkGlyph& glyph, const SkMatrix* deviceMatrix) {
     // See ::detachCache()
     SkGlyphCache* cache;
-    descriptorProc(NULL, 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 +499,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 +518,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 +539,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 +572,7 @@
         return true;
     }
 
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
 
     switch (this->getTextEncoding()) {
@@ -584,6 +596,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 +622,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 +664,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 +742,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 +793,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 +860,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 +910,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));
@@ -843,17 +937,26 @@
         fTextSize = paint->getTextSize();
         fStyle = paint->getStyle();
         fPaint->setStyle(SkPaint::kFill_Style);
+#ifdef SK_BUILD_FOR_ANDROID
+        fGenerationID = fPaint->getGenerationID();
+#endif
     }
 
     ~SkAutoRestorePaintTextSizeAndFrame() {
         fPaint->setStyle(fStyle);
         fPaint->setTextSize(fTextSize);
+#ifdef SK_BUILD_FOR_ANDROID
+        fPaint->setGenerationID(fGenerationID);
+#endif
     }
 
 private:
     SkPaint*        fPaint;
     SkScalar        fTextSize;
     SkPaint::Style  fStyle;
+#ifdef SK_BUILD_FOR_ANDROID
+    uint32_t        fGenerationID;
+#endif
 };
 
 static void set_bounds(const SkGlyph& g, SkRect* bounds) {
@@ -996,7 +1099,7 @@
         zoomPtr = &zoomMatrix;
     }
 
-    SkAutoGlyphCache    autoCache(*this, zoomPtr);
+    SkAutoGlyphCache    autoCache(*this, NULL, zoomPtr);
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkScalar width = 0;
@@ -1072,7 +1175,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 +1263,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 +1305,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 +1405,7 @@
         return;
     }
 
-    SkTextToPathIter    iter(text, length, *this, false, true);
+    SkTextToPathIter    iter(text, length, *this, false);
     SkMatrix            matrix;
     SkScalar            prevXPos = 0;
 
@@ -1312,16 +1415,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 +1500,6 @@
     return true;
 }
 
-#ifdef SK_USE_COLOR_LUMINANCE
 static SkColor computeLuminanceColor(const SkPaint& paint) {
     SkColor c;
     if (!justAColor(paint, &c)) {
@@ -1377,53 +1510,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 +1539,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 +1622,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 +1658,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 +1698,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 +1802,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 +1819,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 +1835,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 +1850,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 +1885,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 +1906,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 +1915,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 +1976,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 +2004,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 +2073,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 +2149,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 +2211,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 +2266,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 +2288,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 +2316,7 @@
         fPaint.setPathEffect(NULL);
     }
 
-    fCache = fPaint.detachCache(NULL);
+    fCache = fPaint.detachCache(NULL, NULL);
 
     SkPaint::Style  style = SkPaint::kFill_Style;
     SkPathEffect*   pe = NULL;
@@ -2147,7 +2346,7 @@
 
     fText = text;
     fStop = text + length;
-    
+
     fXYIndex = paint.isVerticalText() ? 1 : 0;
 }
 
@@ -2155,21 +2354,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 +2405,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 +2428,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 +2449,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/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/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/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/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 f15145d..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"
 
@@ -18,6 +16,5 @@
 
 bool SkPaintFlagsDrawFilter::filter(SkPaint* paint, Type) {
     paint->setFlags((paint->getFlags() & ~fClearFlags) | fSetFlags);
-    return false;
+    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/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/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 fa35239..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"
@@ -439,6 +440,11 @@
         return false;
     }
 
+    if (setjmp(png_jmpbuf(png_ptr)) != 0) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+        return false;
+    }
+
     int bit_depth, color_type, interlace_type;
     png_uint_32 origWidth, origHeight;
     png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
@@ -756,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;
@@ -780,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++) {
@@ -819,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
@@ -930,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) {
@@ -1168,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..a42098c 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -183,33 +183,27 @@
         return false;
     }
 
-    uint32_t bytes_remaining = contentSize;
-    while (bytes_remaining > 0) {
-        const uint32_t bytes_to_read =
-            (bytes_remaining < WEBP_IDECODE_BUFFER_SZ) ?
-                bytes_remaining : WEBP_IDECODE_BUFFER_SZ;
-
+    bool success = true;
+    VP8StatusCode status = VP8_STATUS_SUSPENDED;
+    do {
+        const uint32_t bytes_to_read = WEBP_IDECODE_BUFFER_SZ;
         const size_t bytes_read = stream->read(input, bytes_to_read);
         if (bytes_read == 0) {
+            success = false;
             break;
         }
 
-        VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
-        if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
-            bytes_remaining -= bytes_read;
-        } else {
+        status = WebPIAppend(idec, input, bytes_read);
+        if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) {
+            success = false;
             break;
         }
-    }
+    } while (VP8_STATUS_OK != status);
     srcStorage.free();
     WebPIDelete(idec);
     WebPFreeDecBuffer(&config.output);
 
-    if (bytes_remaining > 0) {
-        return false;
-    } else {
-        return true;
-    }
+    return success;
 }
 
 static bool webp_get_config_resize(WebPDecoderConfig& config,
@@ -560,10 +554,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 +570,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 dddadd0..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;
 
@@ -1118,50 +1107,64 @@
 }
 
 struct HB_UnicodeMapping {
-    HB_Script script;
+    // TODO: when the WebView no longer needs harfbuzz_old, remove
+    HB_Script script_old;
+    hb_script_t script;
     const SkUnichar unicode;
 };
 
 /*
  * The following scripts are not complex fonts and we do not expect them to be parsed by this table
- * HB_Script_Common,
- * HB_Script_Greek,
- * HB_Script_Cyrillic,
- * HB_Script_Hangul
- * HB_Script_Inherited
+ * HB_SCRIPT_COMMON,
+ * HB_SCRIPT_GREEK,
+ * HB_SCRIPT_CYRILLIC,
+ * HB_SCRIPT_HANGUL
+ * HB_SCRIPT_INHERITED
  */
 
-static HB_UnicodeMapping HB_UnicodeMappingArray[] {
-    {HB_Script_Armenian,      0x0531},
-    {HB_Script_Hebrew,        0x0591},
-    {HB_Script_Arabic,        0x0600},
-    {HB_Script_Syriac,        0x0710},
-    {HB_Script_Thaana,        0x0780},
-    {HB_Script_Nko,           0x07C0},
-    {HB_Script_Devanagari,    0x0901},
-    {HB_Script_Bengali,       0x0981},
-    {HB_Script_Gurmukhi,      0x0A10},
-    {HB_Script_Gujarati,      0x0A90},
-    {HB_Script_Oriya,         0x0B10},
-    {HB_Script_Tamil,         0x0B82},
-    {HB_Script_Telugu,        0x0C10},
-    {HB_Script_Kannada,       0x0C90},
-    {HB_Script_Malayalam,     0x0D10},
-    {HB_Script_Sinhala,       0x0D90},
-    {HB_Script_Thai,          0x0E01},
-    {HB_Script_Lao,           0x0E81},
-    {HB_Script_Tibetan,       0x0F00},
-    {HB_Script_Myanmar,       0x1000},
-    {HB_Script_Georgian,      0x10A0},
+static HB_UnicodeMapping HB_UnicodeMappingArray[] = {
+    {HB_Script_Armenian, HB_SCRIPT_ARMENIAN,      0x0531},
+    {HB_Script_Hebrew, HB_SCRIPT_HEBREW,        0x0591},
+    {HB_Script_Arabic, HB_SCRIPT_ARABIC,        0x0600},
+    {HB_Script_Syriac, HB_SCRIPT_SYRIAC,        0x0710},
+    {HB_Script_Thaana, HB_SCRIPT_THAANA,        0x0780},
+    {HB_Script_Nko, HB_SCRIPT_NKO,           0x07C0},
+    {HB_Script_Devanagari, HB_SCRIPT_DEVANAGARI,    0x0901},
+    {HB_Script_Bengali, HB_SCRIPT_BENGALI,       0x0981},
+    {HB_Script_Gurmukhi, HB_SCRIPT_GURMUKHI,      0x0A10},
+    {HB_Script_Gujarati, HB_SCRIPT_GUJARATI,      0x0A90},
+    {HB_Script_Oriya, HB_SCRIPT_ORIYA,         0x0B10},
+    {HB_Script_Tamil, HB_SCRIPT_TAMIL,         0x0B82},
+    {HB_Script_Telugu, HB_SCRIPT_TELUGU,        0x0C10},
+    {HB_Script_Kannada, HB_SCRIPT_KANNADA,       0x0C90},
+    {HB_Script_Malayalam, HB_SCRIPT_MALAYALAM,     0x0D10},
+    {HB_Script_Sinhala, HB_SCRIPT_SINHALA,       0x0D90},
+    {HB_Script_Thai, HB_SCRIPT_THAI,          0x0E01},
+    {HB_Script_Lao, HB_SCRIPT_LAO,           0x0E81},
+    {HB_Script_Tibetan, HB_SCRIPT_TIBETAN,       0x0F00},
+    {HB_Script_Myanmar, HB_SCRIPT_MYANMAR,       0x1000},
+    {HB_Script_Georgian, HB_SCRIPT_GEORGIAN,      0x10A0},
     // we don't currently support HB_Script_Ethiopic, it is a placeholder for an upstream merge
-    //{HB_Script_Ethiopic,    0x1200},
-    {HB_Script_Ogham,         0x1680},
-    {HB_Script_Runic,         0x16A0},
-    {HB_Script_Khmer,         0x1780},
+    //{HB_Script_Ethiopic, HB_SCRIPT_ETHIOPIC,    0x1200},
+    {HB_Script_Ogham, HB_SCRIPT_OGHAM,         0x1680},
+    {HB_Script_Runic, HB_SCRIPT_RUNIC,         0x16A0},
+    {HB_Script_Khmer, HB_SCRIPT_KHMER,         0x1780},
 };
 
+static hb_script_t getHBScriptFromHBScriptOld(HB_Script script_old) {
+    hb_script_t script = HB_SCRIPT_INVALID;
+    int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
+    for (int i = 0; i < numSupportedFonts; i++) {
+        if (script_old == HB_UnicodeMappingArray[i].script_old) {
+            script = HB_UnicodeMappingArray[i].script;
+            break;
+        }
+    }
+    return script;
+}
+
 // returns 0 for "Not Found"
-static SkUnichar getUnicodeFromHBScript(HB_Script script) {
+static SkUnichar getUnicodeFromHBScript(hb_script_t script) {
     SkUnichar unichar = 0;
     int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
     for (int i = 0; i < numSupportedFonts; i++) {
@@ -1174,7 +1177,7 @@
 }
 
 struct TypefaceLookupStruct {
-    HB_Script            script;
+    hb_script_t          script;
     SkTypeface::Style    style;
     SkPaint::FontVariant fontVariant;
     SkTypeface*          typeface;
@@ -1183,21 +1186,21 @@
 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;
 }
 
-SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
+SK_API SkTypeface* SkCreateTypefaceForScriptNG(hb_script_t script, SkTypeface::Style style,
         SkPaint::FontVariant fontVariant) {
     SkTypeface* retTypeface = NULL;
 
@@ -1229,3 +1232,9 @@
     SkSafeRef(retTypeface);
     return retTypeface;
 }
+
+SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
+        SkPaint::FontVariant fontVariant) {
+    return SkCreateTypefaceForScriptNG(getHBScriptFromHBScriptOld(script), style, fontVariant);
+}
+
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_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 46ebb0d..0000000
--- a/src/ports/SkImageRef_ashmem.cpp
+++ /dev/null
@@ -1,247 +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;
-            
-    this->useDefaultMutex();   // we don't need/want the shared imageref mutex
-}
-
-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);
-    }
-    this->useDefaultMutex();   // we don't need/want the shared imageref mutex
-}
-
-SkPixelRef* SkImageRef_ashmem::Create(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkImageRef_ashmem, (buffer));
-}
-
-SK_DEFINE_PIXEL_REF_REGISTRAR(SkImageRef_ashmem)
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/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/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/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/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/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/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/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/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/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/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